Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 60 additions & 68 deletions includes/admin/class-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
],
];

Expand Down Expand Up @@ -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',
],
];

Expand Down Expand Up @@ -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',
],
];

Expand Down
32 changes: 32 additions & 0 deletions includes/admin/class-settings-config.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down Expand Up @@ -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;
}
}
59 changes: 51 additions & 8 deletions includes/admin/class-tags-categories.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand All @@ -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 );

Expand Down Expand Up @@ -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 = '';
Expand All @@ -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 )
)
);
?>
Expand Down
26 changes: 26 additions & 0 deletions views/page-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,32 @@
?>
</td>
</tr>
<tr valign="top">
<th scope="row"><?php esc_html_e( 'Enabled Taxonomies', 'superdraft' ); ?></th>
<td>
<?php
$enabled_taxonomies = $settings['tags_categories']['enabled_taxonomies'] ?? [ 'category', 'post_tag' ];
$public_taxonomies = get_taxonomies( [ 'public' => 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' : '';
?>
<label style="display: block; margin-bottom: 5px;">
<input type="checkbox" name="superdraft_settings[tags_categories][enabled_taxonomies][]"
value="<?php echo esc_attr( $taxonomy->name ); ?>" <?php echo esc_attr( $checked ); ?> />
<?php echo esc_html( $taxonomy->labels->name ); ?>
<code><?php echo esc_html( $taxonomy->name ); ?></code>
</label>
<?php
}
?>
<p class="description"><?php esc_html_e( 'Select which taxonomies to enable for AI auto-select and term suggestions. Built-in taxonomies (Categories and Tags) are enabled by default.', 'superdraft' ); ?></p>
</td>
</tr>
</table>
</div>

Expand Down