$links Existing action links. * @return array Modified action links. */ function plugin_action_links( array $links ): array { $settings_link = sprintf( '%2$s', admin_url( 'options-general.php?page=ai-wp-admin' ), esc_html__( 'Settings', 'ai' ) ); $connectors_link = sprintf( '%2$s', admin_url( 'options-connectors.php' ), esc_html__( 'Connectors', 'ai' ) ); array_unshift( $links, $connectors_link, $settings_link ); return $links; } /** * Gets feature group metadata for the settings UI. * * @since 0.7.0 * * @return array */ function get_settings_feature_groups(): array { $default_groups = array( Experiment_Category::EDITOR => array( 'label' => __( 'Editor Experiments', 'ai' ), 'description' => __( 'AI-powered experiments for the block editor, including content generation and enhancement tools.', 'ai' ), 'order' => 10, ), Experiment_Category::ADMIN => array( 'label' => __( 'Admin Experiments', 'ai' ), 'description' => __( 'AI-powered experiments for the WordPress admin area, including exploration and testing tools.', 'ai' ), 'order' => 20, ), Feature_Category::OTHER => array( 'label' => __( 'Other Features', 'ai' ), 'description' => __( 'Additional AI-powered features.', 'ai' ), 'order' => 90, ), ); /** * Filters feature group metadata used by the settings UI. * * @since 0.7.0 * * @param array $default_groups Feature group metadata keyed by category. */ $filtered_groups = apply_filters( 'wpai_settings_feature_groups', $default_groups ); return is_array( $filtered_groups ) ? $filtered_groups : $default_groups; } /** * Builds feature metadata used by the settings route UI. * * @since 0.7.0 * * @param \WordPress\AI\Features\Registry $registry Feature registry instance. * @return array{ * groups:list, * features:list * } */ function get_settings_feature_metadata( Registry $registry ): array { $group_definitions = get_settings_feature_groups(); $categories_in_use = array(); $features = array(); foreach ( $registry->get_all_features() as $feature ) { $feature_id = $feature::get_id(); $category = $feature->get_category(); if ( ! is_string( $category ) || '' === $category ) { $category = Feature_Category::OTHER; } if ( ! isset( $group_definitions[ $category ] ) ) { $group_definitions[ $category ] = array( 'label' => ucwords( str_replace( array( '-', '_' ), ' ', $category ) ), 'description' => '', 'order' => 100, ); } $categories_in_use[ $category ] = true; $features[] = array( 'id' => $feature_id, 'settingName' => "wpai_feature_{$feature_id}_enabled", 'label' => $feature->get_label(), 'description' => wp_strip_all_tags( $feature->get_description() ), 'category' => $category, 'settingsFields' => $feature->get_settings_fields_metadata(), ); } $groups = array(); foreach ( array_keys( $categories_in_use ) as $category ) { $group = $group_definitions[ $category ] ?? array(); $groups[] = array( 'id' => $category, 'label' => isset( $group['label'] ) && is_string( $group['label'] ) && '' !== $group['label'] ? $group['label'] : ucwords( str_replace( array( '-', '_' ), ' ', $category ) ), 'description' => isset( $group['description'] ) && is_string( $group['description'] ) ? $group['description'] : '', 'order' => isset( $group['order'] ) ? (int) $group['order'] : 100, ); } usort( $groups, static function ( array $first, array $second ): int { if ( $first['order'] === $second['order'] ) { return strcasecmp( (string) $first['label'], (string) $second['label'] ); } return $first['order'] <=> $second['order']; } ); $groups = array_values( array_map( static function ( array $group ): array { unset( $group['order'] ); return $group; }, $groups ) ); $metadata = array( 'groups' => $groups, 'features' => $features, ); /** * Filters settings metadata passed to the settings route client. * * @since 0.7.0 * * @param array{ * groups:list, * features:list * } $metadata Settings UI metadata. * @param \WordPress\AI\Features\Registry $registry Feature registry instance. */ $filtered_metadata = apply_filters( 'wpai_settings_feature_metadata', $metadata, $registry ); return is_array( $filtered_metadata ) ? $filtered_metadata : $metadata; } /** * Loads the plugin after checking requirements. * * @since 0.1.0 */ function load(): void { static $loaded = false; // Prevent loading twice. if ( $loaded ) { return; } // Check version requirements. if ( ! check_php_version() || ! check_wp_version() ) { return; } // Load required files. require_once WPAI_PLUGIN_DIR . 'includes/autoload.php'; require_once WPAI_PLUGIN_DIR . 'includes/helpers.php'; // Load auto-generated wp-build registration if present. if ( file_exists( WPAI_PLUGIN_DIR . 'build/build.php' ) ) { require_once WPAI_PLUGIN_DIR . 'build/build.php'; } // Handle any pending upgrades. ( new Upgrades() )->init(); // Handle deprecated code. ( new Deprecated() )->init(); $loaded = true; // Add plugin action links. add_filter( 'plugin_action_links_' . plugin_basename( WPAI_PLUGIN_FILE ), __NAMESPACE__ . '\plugin_action_links' ); // Hook feature initialization to init. add_action( 'init', __NAMESPACE__ . '\initialize_features', 15 ); } /** * Initializes plugin features. * * @since 0.1.0 */ function initialize_features(): void { try { // Experiments are hooked into our Loader, so we need to register them first. $experiments = new Experiments(); $experiments->init(); $registry = new Registry(); $loader = new Loader( $registry ); $loader->register_features(); $loader->initialize_features(); // Initialize settings registration. $settings_registration = new Settings_Registration( $registry ); $settings_registration->init(); // Register admin settings page menu item. if ( is_admin() ) { Settings_Page::init( $registry ); } // Register our post-related WordPress Abilities. $post_abilities = new Posts(); $post_abilities->register(); add_action( 'wp_abilities_api_categories_init', static function () { /** * Register a generic catch-all category that all * Abilities we register can use. Can re-evaluate this * in the future if we need/want more specific categories. */ wp_register_ability_category( WPAI_DEFAULT_ABILITY_CATEGORY, array( 'label' => __( 'AI', 'ai' ), 'description' => __( 'Various AI features and experiments.', 'ai' ), ), ); } ); } catch ( \Throwable $t ) { _doing_it_wrong( __NAMESPACE__ . '\initialize_features', sprintf( /* translators: %s: Error message. */ esc_html__( 'AI Plugin initialization failed: %s', 'ai' ), esc_html( $t->getMessage() ) ), '0.1.0' ); } } add_action( 'plugins_loaded', __NAMESPACE__ . '\load' ); /** * Triggers when the plugin is activated. * * @since 0.7.0 */ register_activation_hook( WPAI_PLUGIN_FILE, static function (): void { // Load required files. require_once WPAI_PLUGIN_DIR . 'includes/autoload.php'; require_once WPAI_PLUGIN_DIR . 'includes/helpers.php'; Activation::activation_callback(); } );