self::TAB_SLUG, 'label' => __( 'Database', 'acrossai-abilities-manager' ), 'priority' => 35, ); return $tabs; } /** * Register the placeholder settings section that hosts the React mount. * * The vendor TabbedPageRenderer wraps every tab in a Settings API form and calls * `do_settings_sections()` on the tab-scoped page slug, so one section has to exist against that * slug for React to have a container. * * @return void */ public function register_settings(): void { if ( ! class_exists( '\\AcrossAI_Main_Menu\\SettingsPage' ) ) { return; } $renderer = \AcrossAI_Main_Menu\SettingsPage::get_settings_renderer(); if ( ! $renderer ) { return; } add_settings_section( 'acrossai_database_root', '', // Empty heading — the React panels supply their own. array( $this, 'render_root' ), $renderer->tab_page_slug( self::TAB_SLUG ) ); } /** * Section-body render callback. Emits the React mount and a no-JS fallback. * * @return void */ public function render_root(): void { printf( '
', esc_attr( self::ROOT_ID ), esc_html__( 'The Database settings require JavaScript to be enabled.', 'acrossai-abilities-manager' ) ); } /** * Enqueue the React bundle when this tab is the active one. * * @param string $hook_suffix Current admin page hook. * @return void */ public function enqueue_assets( string $hook_suffix ): void { if ( ! $this->is_active_tab_page( $hook_suffix ) ) { return; } $build_url = defined( 'ACROSSAI_ABILITIES_MANAGER_PLUGIN_URL' ) ? \ACROSSAI_ABILITIES_MANAGER_PLUGIN_URL . 'build/' : plugins_url( 'build/', dirname( __DIR__, 2 ) . '/acrossai-abilities-manager.php' ); $build_path = defined( 'ACROSSAI_ABILITIES_MANAGER_PLUGIN_PATH' ) ? \ACROSSAI_ABILITIES_MANAGER_PLUGIN_PATH . 'build/' : dirname( __DIR__, 2 ) . '/build/'; $asset_file = $build_path . 'js/database-settings.asset.php'; if ( ! file_exists( $asset_file ) ) { // Bundle not built yet — nothing to enqueue. return; } $asset = require $asset_file; wp_enqueue_script( 'acrossai-database-settings', $build_url . 'js/database-settings.js', (array) ( $asset['dependencies'] ?? array() ), (string) ( $asset['version'] ?? '0.0.0' ), true ); wp_localize_script( 'acrossai-database-settings', 'acrossaiDatabaseSettings', array( 'restNamespace' => 'acrossai/v1', 'restBase' => 'database-settings', 'nonce' => wp_create_nonce( 'wp_rest' ), ) ); if ( file_exists( $build_path . 'css/database-settings.css' ) ) { wp_enqueue_style( 'acrossai-database-settings', $build_url . 'css/database-settings.css', array(), (string) ( $asset['version'] ?? '0.0.0' ) ); } // The vendor form's Save button is unused here — each panel saves itself over REST, and // leaving a second Save on screen invites the operator to press the one that does nothing. wp_add_inline_style( 'acrossai-database-settings', '.wrap form:has(#' . esc_attr( self::ROOT_ID ) . ') .submit { display: none !important; }' ); } /** * Whether the current admin page is the shared settings page with this tab active. * * @param string $hook_suffix Current admin page hook. * @return bool */ private function is_active_tab_page( string $hook_suffix ): bool { if ( false === strpos( $hook_suffix, 'acrossai-settings' ) ) { return false; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- reading which tab to render, not acting on it. $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : ''; return self::TAB_SLUG === $tab; } }