self::TAB_SLUG, 'label' => __( 'MCP', 'acrossai-mcp-manager' ), 'priority' => 20, ); return $tabs; } /** * Registers settings sections and fields via the WordPress Settings API. * * Hooked to admin_init. Both `option_group` and `page` arguments are set to * the per-tab page slug derived from `SettingsPageRenderer::tab_page_slug()`, * matching the tab-scoped `option_page` value that vendor 0.0.13's * TabbedPageRenderer::render() emits — the options.php whitelist walks only * this tab's registrations on save. * * The vendor package acrossai-co/main-menu is a hard-require in composer.json, * so the `SettingsPage` class is guaranteed present at admin_init and no * class_exists() guard is needed. If that dependency is ever demoted to an * optional integration, revisit DEC-VENDOR-SETTINGS-TAB-INTEGRATION and add * a guard here. * * The renderer is obtained via SettingsPage::get_settings_renderer() (added * in vendor 0.0.13). A fall-through slug matching the vendor's own format is * kept for the edge case where get_settings_renderer() has not yet populated * (e.g. if another consumer disabled the SettingsPage bootstrap for this * request); this keeps admin_init non-fatal. * * @since 0.1.0 * @return void */ public function register_settings(): void { $renderer = \AcrossAI_Main_Menu\SettingsPage::get_settings_renderer(); $page_slug = $renderer ? $renderer->tab_page_slug( self::TAB_SLUG ) : \AcrossAI_Main_Menu\SettingsPage::SETTINGS_SLUG . '-' . sanitize_key( self::TAB_SLUG ); // Vendor 0.0.13 posts each tab's form with `option_page = ` (see TabbedPageRenderer::render), so register against the same // tab-scoped slug — not the shared parent page slug used pre-0.0.13 — // or options.php will reject the save with "not in the allowed options // list". $option_group = $page_slug; // npm / CLI login toggle. register_setting( $option_group, 'acrossai_mcp_npm_login_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean', 'default' => false, ) ); // Uninstall opt-in flag. register_setting( $option_group, 'acrossai_mcp_uninstall_delete_data', array( 'sanitize_callback' => array( $this, 'sanitize_uninstall_flag' ), 'default' => 0, ) ); // Section: npm / CLI Settings. add_settings_section( 'acrossai_mcp_npm_section', __( 'npm / CLI Settings', 'acrossai-mcp-manager' ), array( $this, 'render_npm_section_description' ), $page_slug ); add_settings_field( 'acrossai_mcp_npm_login_enabled', __( 'Enable CLI Connections', 'acrossai-mcp-manager' ), array( $this, 'render_npm_login_field' ), $page_slug, 'acrossai_mcp_npm_section' ); // Section: Uninstall Settings. add_settings_section( 'acrossai_mcp_uninstall_section', __( 'Uninstall Settings', 'acrossai-mcp-manager' ), '__return_false', $page_slug ); add_settings_field( 'acrossai_mcp_uninstall_delete_data', __( 'Delete all data on uninstall', 'acrossai-mcp-manager' ), array( $this, 'render_uninstall_field' ), $page_slug, 'acrossai_mcp_uninstall_section' ); } /** * Renders the description + warning banner for the npm / CLI section. * * @since 0.1.0 * @return void */ public function render_npm_section_description(): void { $auth_url = FrontendAuth::get_base_url(); printf( '

%s

%s ' . /* translators: %s: the frontend authorization URL */ wp_kses_post( __( 'The frontend authorization page at %s contains time-sensitive auth codes and nonces. If your hosting, CDN, or caching plugin caches this URL, authentication will silently fail. Exclude this path from all page-caching rules.', 'acrossai-mcp-manager' ) ) . '

', esc_html__( 'Control whether the npm / npx CLI connection flow is available on server edit pages.', 'acrossai-mcp-manager' ), esc_html__( 'Do not cache the CLI auth URL.', 'acrossai-mcp-manager' ), esc_url( $auth_url ) ); } /** * Renders the npm / CLI login enable checkbox. * * @since 0.1.0 * @return void */ public function render_npm_login_field(): void { $checked = (bool) get_option( 'acrossai_mcp_npm_login_enabled', false ); printf( '

%s

', checked( $checked, true, false ), esc_html__( 'Allow CLI connections via npm / npx', 'acrossai-mcp-manager' ), esc_html__( 'When enabled, the npm tab on each server\'s edit page will display the npx CLI command and let users connect the AcrossAI MCP Manager CLI tool to this site. Users still sign in to WordPress in the browser, then approve access so the CLI can receive a WordPress Application Password without any manual JSON editing. Keep this disabled if you do not want to expose CLI-based connections.', 'acrossai-mcp-manager' ) ); } /** * Sanitizes the uninstall delete data checkbox value. * * Returns 1 when the checkbox is checked, 0 when unchecked or absent. * Browsers do not send unchecked checkboxes, so an absent value means 0. * * @since 0.1.0 * @param mixed $value Raw submitted value. * @return int */ public function sanitize_uninstall_flag( $value ): int { return empty( $value ) ? 0 : 1; } /** * Renders the uninstall delete data checkbox field. * * @since 0.1.0 * @return void */ public function render_uninstall_field(): void { $checked = (bool) get_option( 'acrossai_mcp_uninstall_delete_data', 0 ); printf( '

%s

', checked( $checked, true, false ), esc_html__( 'Delete all data on uninstall', 'acrossai-mcp-manager' ), esc_html__( '⚠ Warning: When checked, uninstalling this plugin will permanently delete all custom database tables and plugin options. This cannot be undone.', 'acrossai-mcp-manager' ) ); } }