$plural, 'singular_name' => $singular, 'search_items' => 'Search ' . $plural, 'all_items' => 'All ' . $plural, 'parent_item' => 'Parent ' . $singular, 'parent_item_colon' => 'Parent ' . $singular . ':', 'edit_item' => 'Edit ' . $singular, 'update_item' => 'Update ' . $singular, 'add_new_item' => 'Add New ' . $singular, 'new_item_name' => 'New ' . $singular . ' Name', 'menu_name' => $plural, ); $args = array( 'labels' => $labels, 'hierarchical' => ! empty( $tax['hierarchical'] ), 'public' => ! empty( $tax['public'] ), 'show_ui' => ! empty( $tax['public'] ), 'show_admin_column' => ! empty( $tax['show_admin_column'] ), 'show_in_rest' => ! empty( $tax['show_in_rest'] ), 'query_var' => true, 'rewrite' => array( 'slug' => $slug ), ); $post_types = isset( $tax['post_types'] ) && is_array( $tax['post_types'] ) ? $tax['post_types'] : array( 'post' ); register_taxonomy( $slug, $post_types, $args ); } } public function admin_menu() { $hook = add_submenu_page( 'metaforge', 'Taxonomies', 'Taxonomies', 'manage_metaforge', 'metaforge_taxonomies', array( $this, 'admin_page' ) ); add_action( "load-{$hook}", array( $this, 'load_screen' ) ); add_submenu_page( null, 'Add Taxonomy', 'Add Taxonomy', 'manage_metaforge', 'metaforge_taxonomies_add', '__return_null' ); } public function load_screen() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : ''; if ( $action !== 'add' && $action !== 'edit' ) { require_once METAFORGE_PATH . 'admin/TaxonomyListTable.php'; $this->list_table = new MetaForge_TaxonomyListTable(); $screen = get_current_screen(); if ( $screen ) { register_column_headers( $screen, $this->list_table->get_columns() ); } } } public function admin_page() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only UI action parameter. $action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : ''; if ( $action === 'add' || $action === 'edit' ) { $this->render_form(); return; } $this->render_list(); } private function render_list() { if ( isset( $this->list_table ) ) { $list_table = $this->list_table; } else { require_once METAFORGE_PATH . 'admin/TaxonomyListTable.php'; $list_table = new MetaForge_TaxonomyListTable(); } $list_table->prepare_items(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $search_val = isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : ''; $is_empty = empty( $list_table->items ) && empty( $search_val ); ?>
views(); ?> display(); ?>
$item ) { if ( ( $item['slug'] ?? '' ) === $search_slug ) { $index = $i; break; } } } elseif ( isset( $_GET['index'] ) ) { $index = absint( wp_unslash( $_GET['index'] ) ); } $new_tax = array( 'plural_label' => sanitize_text_field( wp_unslash( $_POST['plural_label'] ?? '' ) ), 'singular_label' => sanitize_text_field( wp_unslash( $_POST['singular_label'] ?? '' ) ), 'slug' => sanitize_key( wp_unslash( $_POST['slug'] ?? '' ) ), 'hierarchical' => isset( $_POST['hierarchical'] ) ? 1 : 0, 'public' => isset( $_POST['public'] ) ? 1 : 0, 'show_in_rest' => isset( $_POST['show_in_rest'] ) ? 1 : 0, 'show_admin_column' => isset( $_POST['show_admin_column'] ) ? 1 : 0, 'post_types' => isset( $_POST['post_types'] ) && is_array( $_POST['post_types'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['post_types'] ) ) : array(), ); $existing_slugs = array_column( $taxs, 'slug' ); if ( $index !== null && isset( $taxs[ $index ] ) ) { $current_slug = $taxs[ $index ]['slug']; $existing_slugs = array_diff( $existing_slugs, array( $current_slug ) ); } else { $current_slug = ''; } if ( empty( $new_tax['plural_label'] ) ) { $this->form_error = '⚠️ Plural label cannot be empty.'; } elseif ( empty( $new_tax['singular_label'] ) ) { $this->form_error = '⚠️ Singular label cannot be empty.'; } elseif ( empty( $new_tax['slug'] ) ) { $this->form_error = '⚠️ Slug cannot be empty.'; } elseif ( in_array( $new_tax['slug'], $existing_slugs ) || ( $new_tax['slug'] !== $current_slug && taxonomy_exists( $new_tax['slug'] ) ) ) { $this->form_error = '⚠️ A Taxonomy with this slug already exists. Please use a unique slug to prevent conflicts.'; } if ( ! $this->form_error ) { if ( $index !== null && isset( $taxs[ $index ] ) ) { $taxs[ $index ] = $new_tax; } else { $taxs[] = $new_tax; $index = array_key_last( $taxs ); } update_option( 'metaforge_taxonomies', $taxs ); // Flush caches so field groups using taxonomy fields pick up changes immediately delete_transient( 'metaforge_all_field_groups_cache' ); flush_rewrite_rules(); wp_safe_redirect( admin_url( 'admin.php?page=metaforge_taxonomies&action=edit&slug=' . rawurlencode( $new_tax['slug'] ) . '&saved=true' ) ); exit; } } } private function render_form() { $taxs = get_option( 'metaforge_taxonomies', array() ); $index = null; $saved = false; $error = $this->form_error; // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only UI parameters. if ( isset( $_GET['slug'] ) && ! empty( $_GET['slug'] ) ) { $search_slug = sanitize_key( wp_unslash( $_GET['slug'] ) ); foreach ( $taxs as $i => $item ) { if ( ( $item['slug'] ?? '' ) === $search_slug ) { $index = $i; break; } } } elseif ( isset( $_GET['index'] ) ) { $index = intval( wp_unslash( $_GET['index'] ) ); } $tax = ( $index !== null && isset( $taxs[ $index ] ) ) ? $taxs[ $index ] : array(); // phpcs:enable WordPress.Security.NonceVerification.Recommended // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only notice query parameter. if ( isset( $_GET['saved'] ) && sanitize_text_field( wp_unslash( $_GET['saved'] ) ) === 'true' ) { $saved = true; } // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified prior to setting form_error if submitted. if ( $error && isset( $_POST['metaforge_save_tax'] ) ) { $plural = sanitize_text_field( wp_unslash( $_POST['plural_label'] ?? '' ) ); $singular = sanitize_text_field( wp_unslash( $_POST['singular_label'] ?? '' ) ); $slug = sanitize_key( wp_unslash( $_POST['slug'] ?? '' ) ); $hierarchical = isset( $_POST['hierarchical'] ) ? 1 : 0; $public = isset( $_POST['public'] ) ? 1 : 0; $rest = isset( $_POST['show_in_rest'] ) ? 1 : 0; $show_admin_column = isset( $_POST['show_admin_column'] ) ? 1 : 0; $pts = isset( $_POST['post_types'] ) && is_array( $_POST['post_types'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['post_types'] ) ) : array(); } else { // phpcs:enable WordPress.Security.NonceVerification.Missing $plural = $tax['plural_label'] ?? ''; $singular = $tax['singular_label'] ?? ''; $slug = $tax['slug'] ?? ''; $hierarchical = $tax['hierarchical'] ?? 1; $public = $tax['public'] ?? 1; $rest = $tax['show_in_rest'] ?? 1; $show_admin_column = $tax['show_admin_column'] ?? 1; $pts = $tax['post_types'] ?? array( 'post' ); } // Get all post types to allow assigning taxonomy to them $all_post_types = get_post_types( array( 'public' => true ), 'objects' ); if ( isset( $all_post_types['attachment'] ) ) { unset( $all_post_types['attachment'] ); } $is_edit = $index !== null; ?>

← Back

AI Taxonomy Generator

Describe the taxonomy you want to create, and the AI will auto-populate the labels, post type associations, and settings for you!

General Settings
Plural Label e.g. Genres
Singular Label e.g. Genre
Slug e.g. genres (No spaces)
Post Types Attach this taxonomy to:
Advanced Configuration
Options



'Unauthorized' ) ); } $prompt = sanitize_text_field( wp_unslash( $_POST['prompt'] ?? '' ) ); if ( empty( $prompt ) ) { wp_send_json_error( array( 'message' => 'Please provide a prompt.' ) ); } $client = new MetaForge_APIClient(); if ( ! $client->is_ready() ) { wp_send_json_error( array( 'message' => 'AI is not configured. Please set your API key in MetaForge -> AI Settings.' ) ); } $all_post_types = get_post_types( array( 'public' => true ), 'objects' ); $pt_list = array(); foreach ( $all_post_types as $pt ) { $pt_list[] = $pt->name; } $system_prompt = "You are a WordPress developer assistant. Your task is to generate custom taxonomy registration configuration based on the user's prompt. Respond ONLY with a valid raw JSON object. Do not include markdown blocks. JSON format: { \"plural_label\": \"Genres\", \"singular_label\": \"Genre\", \"slug\": \"genre\", \"hierarchical\": 1, \"public\": 1, \"show_admin_column\": 1, \"show_in_rest\": 1, \"post_types\": [\"post\"] } In post_types, select relevant values from: " . implode( ', ', $pt_list ); $response = $client->generate( $system_prompt, 'Generate a taxonomy for: ' . $prompt, true ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'message' => $response->get_error_message() ) ); } $response = trim( $response ); if ( strpos( $response, '```json' ) === 0 ) { $response = trim( substr( $response, 7, -3 ) ); } elseif ( strpos( $response, '```' ) === 0 ) { $response = trim( substr( $response, 3, -3 ) ); } $decoded = json_decode( $response, true ); if ( ! $decoded || empty( $decoded['slug'] ) ) { wp_send_json_error( array( 'message' => 'Failed to parse AI response.' ) ); } $decoded = map_deep( $decoded, 'sanitize_text_field' ); wp_send_json_success( $decoded ); } public function delete_taxonomy_completely( $slug ) { $slug = sanitize_key( $slug ); if ( empty( $slug ) ) { return false; } // 1. Remove from metaforge_taxonomies option $taxs = get_option( 'metaforge_taxonomies', array() ); $found = false; foreach ( $taxs as $index => $tax ) { if ( isset( $tax['slug'] ) && $tax['slug'] === $slug ) { unset( $taxs[ $index ] ); $found = true; break; } } if ( $found ) { update_option( 'metaforge_taxonomies', array_values( $taxs ) ); } // 2. Unregister taxonomy from WP runtime if ( taxonomy_exists( $slug ) ) { unregister_taxonomy( $slug ); } // 3. Remove taxonomy from metaforge_taxonomy_order_settings $order_settings = get_option( 'metaforge_taxonomy_order_settings', array() ); if ( isset( $order_settings['enabled_taxonomies'] ) && is_array( $order_settings['enabled_taxonomies'] ) ) { if ( in_array( $slug, $order_settings['enabled_taxonomies'], true ) ) { $order_settings['enabled_taxonomies'] = array_values( array_diff( $order_settings['enabled_taxonomies'], array( $slug ) ) ); update_option( 'metaforge_taxonomy_order_settings', $order_settings ); } } // 4. Clean up Field Group location rules referencing this taxonomy $field_groups = get_posts( array( 'post_type' => 'metaforge_group', 'posts_per_page' => -1, 'post_status' => 'any', ) ); foreach ( $field_groups as $fg ) { $config = get_post_meta( $fg->ID, '_metaforge_group_config', true ); if ( is_array( $config ) && ! empty( $config['location_rules'] ) ) { $rule_groups_changed = false; $new_location_rules = array(); foreach ( $config['location_rules'] as $rule_group ) { $new_rule_group = array(); foreach ( $rule_group as $rule ) { if ( ( $rule['param'] ?? '' ) === 'taxonomy' && ( $rule['value'] ?? '' ) === $slug ) { $rule_groups_changed = true; } else { $new_rule_group[] = $rule; } } if ( ! empty( $new_rule_group ) ) { $new_location_rules[] = $new_rule_group; } } if ( $rule_groups_changed ) { $config['location_rules'] = $new_location_rules; update_post_meta( $fg->ID, '_metaforge_group_config', $config ); } } } // 5. Clear field groups cache transient delete_transient( 'metaforge_all_field_groups_cache' ); // 6. Flush rewrite rules flush_rewrite_rules(); return $found; } public function ajax_delete() { if ( ! check_ajax_referer( 'metaforge_delete_tax_nonce', 'nonce', false ) && ! check_ajax_referer( 'metaforge_taxonomy_generation_nonce', 'nonce', false ) ) { wp_send_json_error( array( 'message' => 'Security check failed. Please refresh the page and try again.' ) ); } if ( ! current_user_can( 'manage_metaforge' ) ) { wp_send_json_error( array( 'message' => 'Unauthorized' ) ); } $slug = isset( $_POST['slug'] ) ? sanitize_key( $_POST['slug'] ) : ''; if ( empty( $slug ) ) { wp_send_json_error( array( 'message' => 'Invalid taxonomy slug.' ) ); } $success = $this->delete_taxonomy_completely( $slug ); if ( $success ) { wp_send_json_success(); } wp_send_json_error( array( 'message' => 'Taxonomy not found or could not be deleted.' ) ); } }