cpt = $cpt; } /** * Register the top-level menu and submenu pages. * * @since 1.0.0 * * @return void */ public function register_menu(): void { add_menu_page( __( 'AI Conduit', 'ai-conduit' ), __( 'AI Conduit', 'ai-conduit' ), 'manage_wlconduit_tools', 'wlconduit', array( $this, 'render_page' ), 'dashicons-rest-api', 30 ); add_submenu_page( 'wlconduit', __( 'Tools', 'ai-conduit' ), __( 'Tools', 'ai-conduit' ), 'manage_wlconduit_tools', 'wlconduit', array( $this, 'render_page' ) ); add_submenu_page( 'wlconduit', __( 'Instructions', 'ai-conduit' ), __( 'Instructions', 'ai-conduit' ), 'manage_wlconduit_tools', 'wlconduit#/instructions', array( $this, 'render_page' ) ); } /** * Render the React app mount point. * * @since 1.0.0 * * @return void */ public function render_page(): void { echo wp_kses_post( '
' ); } /** * Enqueue React app scripts, styles, and localized data. * * @since 1.0.0 * * @param string $hook The current admin page hook suffix. * @return void */ public function enqueue_assets( string $hook ): void { if ( 'toplevel_page_wlconduit' !== $hook ) { return; } $asset_file = WLCONDUIT_PLUGIN_DIR . 'build/index.asset.php'; if ( file_exists( $asset_file ) ) { $asset = require $asset_file; } else { $asset = array( 'dependencies' => array(), 'version' => WLCONDUIT_VERSION, ); } wp_enqueue_script( 'wlconduit-admin', WLCONDUIT_PLUGIN_URL . 'build/index.js', $asset['dependencies'], $asset['version'], true ); wp_enqueue_style( 'wlconduit-admin', WLCONDUIT_PLUGIN_URL . 'build/index.css', array( 'wp-components' ), $asset['version'] ); $post_types = get_post_types( array(), 'objects' ); $post_type_list = array(); foreach ( $post_types as $pt ) { $post_type_list[] = array( 'name' => $pt->name, 'label' => $pt->label, 'internal' => ! $pt->public, ); } wp_localize_script( 'wlconduit-admin', 'wlconduitData', array( 'restUrl' => rest_url( 'wlconduit/v1/' ), 'mcpEndpoint' => rest_url( 'wlconduit-mcp/v1' ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'postTypes' => $post_type_list, 'roles' => self::get_roles_list(), 'blocks' => Wlconduit_Block_Registry::get_all_definitions(), 'tools' => $this->cpt->get_all_tools(), 'isAdmin' => current_user_can( 'manage_options' ), 'profileUrl' => admin_url( 'profile.php#application-passwords-section' ), 'siteName' => get_bloginfo( 'name' ), 'currentUser' => wp_get_current_user()->user_login, 'settings' => array( 'cache_expiry_terms' => absint( get_option( 'wlconduit_cache_expiry_terms', 30 ) ), 'cache_expiry_meta_keys' => absint( get_option( 'wlconduit_cache_expiry_meta_keys', 30 ) ), 'log_retention_days' => absint( get_option( 'wlconduit_log_retention_days', 30 ) ), 'logging_enabled' => get_option( 'wlconduit_logging_enabled', '1' ) === '1', 'rate_limiting_enabled' => get_option( 'wlconduit_rate_limiting_enabled', '1' ) === '1', 'rate_limit' => absint( get_option( 'wlconduit_rate_limit', 20 ) ), 'rate_block_duration' => absint( get_option( 'wlconduit_rate_block_duration', 60 ) ), ), ) ); } /** * Get all registered roles for the role selector. * * @since 1.0.0 * * @return array List of role objects with name and label. */ private static function get_roles_list(): array { $wp_roles = wp_roles(); $roles = array(); foreach ( $wp_roles->get_names() as $slug => $name ) { $roles[] = array( 'name' => $slug, 'label' => translate_user_role( $name ), ); } return $roles; } }