From 3f8410a8dfc4cab8c15e8b310ac964a3d08727c2 Mon Sep 17 00:00:00 2001 From: Remi Date: Sun, 22 Mar 2026 02:02:10 +0100 Subject: [PATCH] Update models and add custom taxonomy support **Model Updates:** - Pruned deprecated text models (gpt-3.5-turbo, gpt-4-turbo, o1-preview, older Claude 3.x dated versions, grok-2-1212) - Added newer models: GPT Image 2, Claude Opus 4.6, Claude Sonnet 4.5 - Updated image models: Added GPT Image 2, FLUX 1.1 Pro Ultra, reorganized edit models **Custom Taxonomy Support:** - Added 'enabled_taxonomies' setting to select which taxonomies support AI features - Updated bulk actions to work with any enabled taxonomy (not just Categories/Tags) - Updated suggestion form to register for all enabled taxonomies - Updated REST endpoint and AJAX handlers for custom taxonomy support - Added taxonomy selection UI in settings page - Dynamic taxonomy labels in bulk process notices --- includes/admin/class-admin.php | 128 +++++++++++------------ includes/admin/class-settings-config.php | 32 ++++++ includes/admin/class-tags-categories.php | 59 +++++++++-- views/page-settings.php | 26 +++++ 4 files changed, 169 insertions(+), 76 deletions(-) diff --git a/includes/admin/class-admin.php b/includes/admin/class-admin.php index 1cee86a..1702fc1 100644 --- a/includes/admin/class-admin.php +++ b/includes/admin/class-admin.php @@ -395,42 +395,32 @@ public static function get_models() { 'OpenAI' => [ 'gpt-5.1' => 'GPT-5.1', 'gpt-5' => 'GPT-5', - 'gpt-5-mini' => 'GPT-5 mini', - 'gpt-5-nano' => 'GPT-5 nano', - 'gpt-5-chat-latest' => 'ChatGPT-5-latest', + 'gpt-5-mini' => 'GPT-5 Mini', + 'gpt-5-nano' => 'GPT-5 Nano', + 'gpt-5-chat-latest' => 'ChatGPT-5 Latest', 'gpt-4.5-preview' => 'GPT-4.5 Preview', 'gpt-4.1' => 'GPT-4.1', - 'gpt-4.1-mini' => 'GPT-4.1 mini', - 'gpt-4.1-nano' => 'GPT-4.1 nano', + 'gpt-4.1-mini' => 'GPT-4.1 Mini', + 'gpt-4.1-nano' => 'GPT-4.1 Nano', 'gpt-4o' => 'GPT-4o', - 'gpt-4o-mini' => 'GPT-4o mini', - 'chatgpt-4o-latest' => 'ChatGPT-4o-latest', + 'gpt-4o-mini' => 'GPT-4o Mini', + 'chatgpt-4o-latest' => 'ChatGPT-4o Latest', 'o1' => 'o1', - 'o1-preview' => 'o1-preview', - 'o3-mini' => 'o3-mini', 'o3' => 'o3', - 'o4-mini' => 'o4-mini', - 'gpt-4-turbo' => 'GPT-4 Turbo', - 'gpt-3.5-turbo' => 'GPT-3.5 Turbo', + 'o3-mini' => 'o3 Mini', + 'o4-mini' => 'o4 Mini', ], 'Anthropic' => [ - 'claude-opus-4-5-20251101' => 'Claude Opus 4.5-20251101', - 'claude-sonnet-4-5-20250929' => 'Claude Sonnet 4.5-20250929', - 'claude-haiku-4-5-20251001' => 'Claude Haiku 4.5-20251001', - 'claude-opus-4-1-20250805' => 'Claude Opus 4.1-20250805', - 'claude-opus-4-20250514' => 'Claude Opus 4-20250514', - 'claude-sonnet-4-20250514' => 'Claude Sonnet 4-20250514', - 'claude-3-7-sonnet-latest' => 'Claude 3.7 Sonnet-latest', - 'claude-3-7-sonnet-20250219' => 'Claude 3.7 Sonnet-20250219', - 'claude-3-7-sonnet-thinking' => 'Claude 3.7 Sonnet Thinking', - 'claude-3-5-sonnet-latest' => 'Claude 3.5 Sonnet-latest', - 'claude-3-5-sonnet-20241022' => 'Claude 3.5 Sonnet-20241022', - 'claude-3-5-sonnet-20240620' => 'Claude 3.5 Sonnet-20240620', - 'claude-3-5-haiku-latest' => 'Claude 3.5 Haiku-latest', - 'claude-3-5-haiku-20241022' => 'Claude 3.5 Haiku-20241022', - 'claude-3-opus-20240229' => 'Claude 3 Opus-20240229', - 'claude-3-sonnet-20240229' => 'Claude 3 Sonnet-20240229', - 'claude-3-haiku-20240307' => 'Claude 3 Haiku-20240307', + 'claude-opus-4-6-20260101' => 'Claude Opus 4.6', + 'claude-opus-4-5-20251101' => 'Claude Opus 4.5', + 'claude-opus-4-1-20250805' => 'Claude Opus 4.1', + 'claude-opus-4-20250514' => 'Claude Opus 4', + 'claude-sonnet-4-5-20250929' => 'Claude Sonnet 4.5', + 'claude-sonnet-4-20250514' => 'Claude Sonnet 4', + 'claude-3-7-sonnet-latest' => 'Claude 3.7 Sonnet Latest', + 'claude-3-7-sonnet-20250219' => 'Claude 3.7 Sonnet', + 'claude-3-5-sonnet-latest' => 'Claude 3.5 Sonnet Latest', + 'claude-3-5-haiku-latest' => 'Claude 3.5 Haiku Latest', ], 'Google' => [ 'gemini-3-pro-preview' => 'Gemini 3 Pro Preview', @@ -452,7 +442,6 @@ public static function get_models() { 'grok-4-0709' => 'Grok 4-0709', 'grok-3' => 'Grok 3', 'grok-3-mini' => 'Grok 3 Mini', - 'grok-2-1212' => 'Grok 2-1212', ], ]; @@ -544,36 +533,38 @@ public static function get_model_select( $module, $model_key = 'model' ) { if ( 'image_model' === $model_key ) { $image_models = [ 'Google' => [ - 'gemini-2.5-flash-image' => 'Gemini 2.5 Flash Image (Nano-Banana)', + 'gemini-2.5-flash-image' => 'Gemini 2.5 Flash Image (Nano-Banana)', 'gemini-3-pro-image-preview' => 'Gemini 3 Pro Image (Nano-Banana Pro)', ], 'OpenAI' => [ - 'gpt-image-1' => 'GPT Image 1', + 'gpt-image-2' => 'GPT Image 2', + 'gpt-image-1' => 'GPT Image 1', 'gpt-image-1-mini' => 'GPT Image 1 Mini', ], 'Replicate' => [ - 'google/nano-banana-pro' => 'Gemini 3 Pro Image (Nano-Banana Pro)', - 'google/nano-banana' => 'Gemini 2.5 Flash Image (Nano-Banana)', - 'google/imagen-4' => 'Imagen 4', - 'google/imagen-4-ultra' => 'Imagen 4 Ultra', - 'google/imagen-4-fast' => 'Imagen 4 Fast', - 'google/imagen-3' => 'Imagen 3', - 'google/imagen-3-fast' => 'Imagen 3 Fast', - 'black-forest-labs/flux-2-pro' => 'Flux 2 Pro', - 'black-forest-labs/flux-2-dev' => 'Flux 2 Dev', - 'black-forest-labs/flux-2-flex' => 'Flux 2 Flex', - 'black-forest-labs/flux-1.1-pro' => 'Flux 1.1 Pro', - 'black-forest-labs/flux-dev' => 'Flux Dev', - 'black-forest-labs/flux-schnell' => 'Flux Schnell', - 'black-forest-labs/flux-pro' => 'Flux Pro', - 'recraft-ai/recraft-v3' => 'Recraft v3', - 'ideogram-ai/ideogram-v2a' => 'Ideogram v2a', - 'ideogram-ai/ideogram-v3-turbo' => 'Ideogram v3 Turbo', - 'ideogram-ai/ideogram-v3-quality' => 'Ideogram v3 Quality', - 'ideogram-ai/ideogram-v3-balanced' => 'Ideogram v3 Balanced', - 'bytedance/seedream-4.5' => 'Seedream 4.5', - 'bytedance/seedream-4' => 'Seedream 4', - 'qwen/qwen-image' => 'Qwen Image', + 'google/nano-banana-pro' => 'Gemini 3 Pro Image (Nano-Banana Pro)', + 'google/nano-banana' => 'Gemini 2.5 Flash Image (Nano-Banana)', + 'google/imagen-4' => 'Imagen 4', + 'google/imagen-4-ultra' => 'Imagen 4 Ultra', + 'google/imagen-4-fast' => 'Imagen 4 Fast', + 'google/imagen-3' => 'Imagen 3', + 'google/imagen-3-fast' => 'Imagen 3 Fast', + 'black-forest-labs/flux-2-pro' => 'FLUX 2 Pro', + 'black-forest-labs/flux-2-dev' => 'FLUX 2 Dev', + 'black-forest-labs/flux-2-flex' => 'FLUX 2 Flex', + 'black-forest-labs/flux-1.1-pro' => 'FLUX 1.1 Pro', + 'black-forest-labs/flux-1.1-pro-ultra'=> 'FLUX 1.1 Pro Ultra', + 'black-forest-labs/flux-dev' => 'FLUX Dev', + 'black-forest-labs/flux-schnell' => 'FLUX Schnell', + 'black-forest-labs/flux-pro' => 'FLUX Pro', + 'recraft-ai/recraft-v3' => 'Recraft v3', + 'ideogram-ai/ideogram-v3-turbo' => 'Ideogram v3 Turbo', + 'ideogram-ai/ideogram-v3-quality' => 'Ideogram v3 Quality', + 'ideogram-ai/ideogram-v3-balanced' => 'Ideogram v3 Balanced', + 'ideogram-ai/ideogram-v2a' => 'Ideogram v2a', + 'bytedance/seedream-4.5' => 'Seedream 4.5', + 'bytedance/seedream-4' => 'Seedream 4', + 'qwen/qwen-image' => 'Qwen Image', ], ]; @@ -616,25 +607,26 @@ public static function get_model_select( $module, $model_key = 'model' ) { $no_edit_option = [ '' => __( 'No image edits', 'superdraft' ) ]; // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssignmentAlignment -- Single assignment clarity. $grouped_edit_models = [ 'Google' => [ - 'gemini-3-pro-image-preview' => 'Gemini 3 Pro Image (Nano-Banana Pro)', - 'gemini-2.5-flash-image' => 'Gemini 2.5 Flash Image (Nano-Banana)', + 'gemini-3-pro-image-preview' => 'Gemini 3 Pro Image (Nano-Banana Pro)', + 'gemini-2.5-flash-image' => 'Gemini 2.5 Flash Image (Nano-Banana)', ], 'OpenAI' => [ - 'gpt-image-1' => 'GPT Image 1', + 'gpt-image-2' => 'GPT Image 2', + 'gpt-image-1' => 'GPT Image 1', 'gpt-image-1-mini' => 'GPT Image 1 Mini', ], 'Replicate' => [ - 'google/nano-banana-pro' => 'Gemini 3 Pro Image (Nano-Banana Pro)', - 'qwen/qwen-image-edit' => 'Qwen Image Edit', - 'bytedance/seededit-3.0' => 'SeedEdit 3.0', - 'bytedance/seedream-4' => 'Seedream 4', - 'bytedance/seedream-4.5' => 'Seedream 4.5', - 'google/nano-banana' => 'Nano-Banana', - 'black-forest-labs/flux-kontext-max' => 'FLUX Kontext Max', - 'black-forest-labs/flux-kontext-dev' => 'FLUX Kontext Dev', - 'black-forest-labs/flux-2-pro' => 'Flux 2 Pro', - 'black-forest-labs/flux-2-dev' => 'Flux 2 Dev', - 'black-forest-labs/flux-2-flex' => 'Flux 2 Flex', + 'google/nano-banana-pro' => 'Gemini 3 Pro Image (Nano-Banana Pro)', + 'google/nano-banana' => 'Gemini 2.5 Flash Image (Nano-Banana)', + 'black-forest-labs/flux-kontext-max' => 'FLUX Kontext Max', + 'black-forest-labs/flux-kontext-dev' => 'FLUX Kontext Dev', + 'black-forest-labs/flux-2-pro' => 'FLUX 2 Pro', + 'black-forest-labs/flux-2-dev' => 'FLUX 2 Dev', + 'black-forest-labs/flux-2-flex' => 'FLUX 2 Flex', + 'bytedance/seedream-4.5' => 'Seedream 4.5', + 'bytedance/seedream-4' => 'Seedream 4', + 'bytedance/seededit-3.0' => 'SeedEdit 3.0', + 'qwen/qwen-image-edit' => 'Qwen Image Edit', ], ]; diff --git a/includes/admin/class-settings-config.php b/includes/admin/class-settings-config.php index ec7114a..021cbce 100644 --- a/includes/admin/class-settings-config.php +++ b/includes/admin/class-settings-config.php @@ -47,6 +47,11 @@ public static function get_module_settings() { 'default' => 20, 'sanitize' => 'absint', ], + 'enabled_taxonomies' => [ + 'type' => 'array', + 'default' => [ 'category', 'post_tag' ], + 'sanitize' => [ __CLASS__, 'sanitize_taxonomy_array' ], + ], ], 'writing_tips' => [ 'enabled' => [ @@ -210,4 +215,31 @@ public static function get_default_api_keys() { } return $defaults; } + + /** + * Sanitize taxonomy array. + * + * @param array $input The input array to sanitize. + * @return array Sanitized array of valid taxonomy names. + */ + public static function sanitize_taxonomy_array( $input ) { + if ( ! is_array( $input ) ) { + return [ 'category', 'post_tag' ]; + } + + $sanitized = []; + foreach ( $input as $taxonomy ) { + $taxonomy = sanitize_text_field( $taxonomy ); + if ( taxonomy_exists( $taxonomy ) ) { + $sanitized[] = $taxonomy; + } + } + + // Always ensure at least one taxonomy is enabled. + if ( empty( $sanitized ) ) { + return [ 'category', 'post_tag' ]; + } + + return $sanitized; + } } diff --git a/includes/admin/class-tags-categories.php b/includes/admin/class-tags-categories.php index b3117cc..9b3bbc9 100644 --- a/includes/admin/class-tags-categories.php +++ b/includes/admin/class-tags-categories.php @@ -41,13 +41,26 @@ public function __construct() { add_action( 'admin_init', [ $this, 'handle_cancel_bulk_process' ] ); add_action( 'superdraft_bulk_process_completed', [ $this, 'cleanup_bulk_process_data' ] ); - // Taxonomy suggestions. - add_action( 'category_add_form_fields', [ $this, 'render_suggestion_form' ] ); - add_action( 'post_tag_add_form_fields', [ $this, 'render_suggestion_form' ] ); + // Taxonomy suggestions - register for all public taxonomies. + add_action( 'admin_init', [ $this, 'register_taxonomy_suggestion_hooks' ] ); add_action( 'wp_ajax_superdraft_suggest_terms', [ $this, 'handle_suggest_terms' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_taxonomy_assets' ] ); } + /** + * Register taxonomy suggestion hooks for all enabled taxonomies. + */ + public function register_taxonomy_suggestion_hooks() { + $settings = get_option( 'superdraft_settings', [] ); + $enabled_taxonomies = $settings['tags_categories']['enabled_taxonomies'] ?? [ 'category', 'post_tag' ]; + + foreach ( $enabled_taxonomies as $taxonomy ) { + if ( taxonomy_exists( $taxonomy ) ) { + add_action( "{$taxonomy}_add_form_fields", [ $this, 'render_suggestion_form' ] ); + } + } + } + /** * Register the taxonomy auto-select endpoint. */ @@ -154,8 +167,23 @@ public function register_bulk_action( $bulk_actions ) { return $bulk_actions; } - $bulk_actions['superdraft_auto_select_categories'] = __( 'Auto-select Categories', 'superdraft' ); - $bulk_actions['superdraft_auto_select_tags'] = __( 'Auto-select Tags', 'superdraft' ); + $settings = get_option( 'superdraft_settings', [] ); + $enabled_taxonomies = $settings['tags_categories']['enabled_taxonomies'] ?? [ 'category', 'post_tag' ]; + + foreach ( $enabled_taxonomies as $taxonomy ) { + if ( ! taxonomy_exists( $taxonomy ) ) { + continue; + } + + $taxonomy_obj = get_taxonomy( $taxonomy ); + $label = $taxonomy_obj->labels->name ?? $taxonomy; + + // Add bulk action for this taxonomy. + $action_key = "superdraft_auto_select_{$taxonomy}"; + /* translators: %s: Taxonomy label */ + $bulk_actions[ $action_key ] = sprintf( __( 'Auto-select %s', 'superdraft' ), $label ); + } + return $bulk_actions; } @@ -182,8 +210,15 @@ public function handle_bulk_action( $redirect_url, $action, $post_ids ) { */ $interval = apply_filters( 'superdraft_bulk_process_interval', 60, $action, $post_ids ); - if ( 'superdraft_auto_select_categories' === $action || 'superdraft_auto_select_tags' === $action ) { - $taxonomy = ( 'superdraft_auto_select_categories' === $action ) ? 'category' : 'post_tag'; + // Check if this is a superdraft auto-select action. + if ( strpos( $action, 'superdraft_auto_select_' ) === 0 ) { + // Extract taxonomy from action name. + $taxonomy = str_replace( 'superdraft_auto_select_', '', $action ); + + // Validate taxonomy exists. + if ( ! taxonomy_exists( $taxonomy ) ) { + return $redirect_url; + } // Calculate the first scheduled time (start in 1 minute). $schedule_time = time() + $interval; @@ -195,6 +230,7 @@ public function handle_bulk_action( $redirect_url, $action, $post_ids ) { 'start_time' => time(), 'post_ids' => $post_ids, 'last_scheduled' => $schedule_time + ( ( count( $post_ids ) - 1 ) * $interval ), // Store when the last action will run. + 'taxonomy' => $taxonomy, ]; update_option( 'superdraft_bulk_process_data', $bulk_data ); @@ -419,6 +455,13 @@ public function display_bulk_process_notice() { $percentage = round( ( $processed / $total ) * 100 ); $taxonomy = isset( $bulk_data['taxonomy'] ) ? $bulk_data['taxonomy'] : 'category'; + // Get taxonomy label. + $taxonomy_label = __( 'Terms', 'superdraft' ); + if ( taxonomy_exists( $taxonomy ) ) { + $taxonomy_obj = get_taxonomy( $taxonomy ); + $taxonomy_label = $taxonomy_obj->labels->name ?? $taxonomy; + } + // Calculate remaining time. $completion_time = isset( $bulk_data['last_scheduled'] ) ? $bulk_data['last_scheduled'] : 0; $time_remaining = ''; @@ -440,7 +483,7 @@ public function display_bulk_process_notice() { sprintf( // translators: %s is the taxonomy name. __( 'Superdraft is processing auto-select for posts (%s)', 'superdraft' ), - ( 'category' === $taxonomy ) ? __( 'Categories', 'superdraft' ) : __( 'Tags', 'superdraft' ) + esc_html( $taxonomy_label ) ) ); ?> diff --git a/views/page-settings.php b/views/page-settings.php index 31f9c96..9561bb5 100644 --- a/views/page-settings.php +++ b/views/page-settings.php @@ -107,6 +107,32 @@ ?> + + + + true ], 'objects' ); + + foreach ( $public_taxonomies as $taxonomy ) { + // Skip taxonomies that don't have a UI. + if ( false === $taxonomy->show_ui ) { + continue; + } + $checked = in_array( $taxonomy->name, $enabled_taxonomies, true ) ? 'checked' : ''; + ?> + + +

+ +