$plural, 'singular_name' => $singular, 'menu_name' => $plural, 'name_admin_bar' => $singular, 'add_new' => 'Add New', 'add_new_item' => 'Add New ' . $singular, 'new_item' => 'New ' . $singular, 'edit_item' => 'Edit ' . $singular, 'view_item' => 'View ' . $singular, 'all_items' => 'All ' . $plural, 'search_items' => 'Search ' . $plural, 'parent_item_colon' => 'Parent ' . $plural . ':', 'not_found' => 'No ' . strtolower( $plural ) . ' found.', 'not_found_in_trash' => 'No ' . strtolower( $plural ) . ' found in Trash.', ); $args = array( 'labels' => $labels, 'public' => ! empty( $cpt['public'] ), 'publicly_queryable' => ! empty( $cpt['public'] ), 'show_ui' => ! empty( $cpt['public'] ), 'show_in_menu' => ! empty( $cpt['public'] ), 'query_var' => true, 'rewrite' => array( 'slug' => $slug ), 'capability_type' => 'post', 'has_archive' => ! empty( $cpt['has_archive'] ), 'hierarchical' => false, 'menu_position' => null, 'menu_icon' => sanitize_text_field( $cpt['menu_icon'] ?? 'dashicons-admin-post' ), 'show_in_rest' => ! empty( $cpt['show_in_rest'] ), 'supports' => isset( $cpt['supports'] ) && is_array( $cpt['supports'] ) ? $cpt['supports'] : array( 'title', 'editor' ), ); register_post_type( $slug, $args ); } } public function admin_menu() { $hook = add_submenu_page( 'metaforge', 'Post Types', 'Post Types', 'manage_metaforge', 'metaforge_post_types', array( $this, 'admin_page' ) ); add_action( "load-{$hook}", array( $this, 'load_screen' ) ); add_submenu_page( null, 'Add Post Type', 'Add Post Type', 'manage_metaforge', 'metaforge_post_types_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/PostTypeListTable.php'; $this->list_table = new MetaForge_PostTypeListTable(); $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/PostTypeListTable.php'; $list_table = new MetaForge_PostTypeListTable(); } $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_cpt = 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'] ?? '' ) ), 'menu_icon' => sanitize_text_field( wp_unslash( $_POST['menu_icon'] ?? 'dashicons-admin-post' ) ), 'public' => isset( $_POST['public'] ) ? 1 : 0, 'has_archive' => isset( $_POST['has_archive'] ) ? 1 : 0, 'show_in_rest' => isset( $_POST['show_in_rest'] ) ? 1 : 0, 'supports' => isset( $_POST['supports'] ) && is_array( $_POST['supports'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['supports'] ) ) : array(), ); $existing_slugs = array_column( $cpts, 'slug' ); if ( $index !== null && isset( $cpts[ $index ] ) ) { $current_slug = $cpts[ $index ]['slug']; $existing_slugs = array_diff( $existing_slugs, array( $current_slug ) ); } else { $current_slug = ''; } if ( empty( $new_cpt['plural_label'] ) ) { $this->form_error = '⚠️ Plural label cannot be empty.'; } elseif ( empty( $new_cpt['singular_label'] ) ) { $this->form_error = '⚠️ Singular label cannot be empty.'; } elseif ( empty( $new_cpt['slug'] ) ) { $this->form_error = '⚠️ Slug cannot be empty.'; } elseif ( in_array( $new_cpt['slug'], $existing_slugs ) || ( $new_cpt['slug'] !== $current_slug && post_type_exists( $new_cpt['slug'] ) ) ) { $this->form_error = '⚠️ A Post Type with this slug already exists. Please use a unique slug to prevent conflicts.'; } if ( ! $this->form_error ) { if ( $index !== null && isset( $cpts[ $index ] ) ) { $cpts[ $index ] = $new_cpt; } else { $cpts[] = $new_cpt; $index = array_key_last( $cpts ); } update_option( 'metaforge_post_types', $cpts ); flush_rewrite_rules(); wp_safe_redirect( admin_url( 'admin.php?page=metaforge_post_types&action=edit&slug=' . rawurlencode( $new_cpt['slug'] ) . '&saved=true' ) ); exit; } } } private function render_form() { $cpts = get_option( 'metaforge_post_types', 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 ( $cpts as $i => $item ) { if ( ( $item['slug'] ?? '' ) === $search_slug ) { $index = $i; break; } } } elseif ( isset( $_GET['index'] ) ) { $index = intval( wp_unslash( $_GET['index'] ) ); } $cpt = ( $index !== null && isset( $cpts[ $index ] ) ) ? $cpts[ $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_cpt'] ) ) { $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'] ?? '' ) ); $icon = sanitize_text_field( wp_unslash( $_POST['menu_icon'] ?? 'dashicons-admin-post' ) ); $public = isset( $_POST['public'] ) ? 1 : 0; $archive = isset( $_POST['has_archive'] ) ? 1 : 0; $rest = isset( $_POST['show_in_rest'] ) ? 1 : 0; $supports = isset( $_POST['supports'] ) && is_array( $_POST['supports'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['supports'] ) ) : array(); } else { // phpcs:enable WordPress.Security.NonceVerification.Missing $plural = $cpt['plural_label'] ?? ''; $singular = $cpt['singular_label'] ?? ''; $slug = $cpt['slug'] ?? ''; $icon = $cpt['menu_icon'] ?? 'dashicons-admin-post'; $public = $cpt['public'] ?? 1; $archive = $cpt['has_archive'] ?? 0; $rest = $cpt['show_in_rest'] ?? 1; $supports = $cpt['supports'] ?? array( 'title', 'editor', 'thumbnail' ); } $is_edit = $index !== null; ?>

← Back

AI Post Type Generator

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

General Settings
Plural Label e.g. Projects
Singular Label e.g. Project
Slug e.g. projects (No spaces)
Menu Icon Dashicons class
(e.g. dashicons-portfolio)
Advanced Configuration
Options


Supports
'Title', 'editor' => 'Editor', 'author' => 'Author', 'thumbnail' => 'Featured Image', 'excerpt' => 'Excerpt', 'trackbacks' => 'Trackbacks', 'custom-fields' => 'Custom Fields', 'comments' => 'Comments', 'revisions' => 'Revisions', 'page-attributes' => 'Page Attributes', ); foreach ( $all_supports as $val => $lbl ) : ?>
'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.' ) ); } $system_prompt = "You are a WordPress developer assistant. Your task is to generate custom post type 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\": \"Projects\", \"singular_label\": \"Project\", \"slug\": \"project\", \"menu_icon\": \"dashicons-portfolio\", \"public\": 1, \"has_archive\": 0, \"show_in_rest\": 1, \"supports\": [\"title\", \"editor\", \"thumbnail\", \"excerpt\"] } Use a valid dashicons class for menu_icon. In supports, choose from: title, editor, author, thumbnail, excerpt, trackbacks, custom-fields, comments, revisions, page-attributes. Note: has_archive must be 0 by default unless specifically requested in the prompt."; $response = $client->generate( $system_prompt, 'Generate a CPT 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' ); // has_archive should be unchecked (0) by default unless specifically requested if ( ! isset( $decoded['has_archive'] ) || ! preg_match( '/\b(archive|archives)\b/i', $prompt ) ) { $decoded['has_archive'] = 0; } else { $decoded['has_archive'] = ! empty( $decoded['has_archive'] ) ? 1 : 0; } wp_send_json_success( $decoded ); } public function delete_post_type_completely( $slug ) { $slug = sanitize_key( $slug ); if ( empty( $slug ) ) { return false; } // 1. Remove from metaforge_post_types option $cpts = get_option( 'metaforge_post_types', array() ); $found = false; foreach ( $cpts as $index => $cpt ) { if ( isset( $cpt['slug'] ) && $cpt['slug'] === $slug ) { unset( $cpts[ $index ] ); $found = true; break; } } if ( $found ) { update_option( 'metaforge_post_types', array_values( $cpts ) ); } // 2. Unregister post type if active in WP runtime if ( post_type_exists( $slug ) ) { unregister_post_type( $slug ); } // 3. Remove post_type from registered taxonomies in metaforge_taxonomies option $taxs = get_option( 'metaforge_taxonomies', array() ); $tax_updated = false; foreach ( $taxs as &$tax ) { if ( isset( $tax['post_types'] ) && is_array( $tax['post_types'] ) ) { if ( in_array( $slug, $tax['post_types'], true ) ) { $tax['post_types'] = array_values( array_diff( $tax['post_types'], array( $slug ) ) ); $tax_updated = true; } } } if ( $tax_updated ) { update_option( 'metaforge_taxonomies', $taxs ); } // 4. Remove post_type from metaforge_post_order_settings $order_settings = get_option( 'metaforge_post_order_settings', array() ); if ( isset( $order_settings['enabled_post_types'] ) && is_array( $order_settings['enabled_post_types'] ) ) { if ( in_array( $slug, $order_settings['enabled_post_types'], true ) ) { $order_settings['enabled_post_types'] = array_values( array_diff( $order_settings['enabled_post_types'], array( $slug ) ) ); update_option( 'metaforge_post_order_settings', $order_settings ); } } // 5. Clean up Field Group location rules referencing this post_type $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'] ?? '' ) === 'post_type' && ( $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 ); } } } // 6. Clear field groups cache transient delete_transient( 'metaforge_all_field_groups_cache' ); // 7. Flush rewrite rules flush_rewrite_rules(); return $found; } public function ajax_delete() { if ( ! check_ajax_referer( 'metaforge_delete_cpt_nonce', 'nonce', false ) && ! check_ajax_referer( 'metaforge_cpt_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 post type slug.' ) ); } $success = $this->delete_post_type_completely( $slug ); if ( $success ) { wp_send_json_success(); } wp_send_json_error( array( 'message' => 'Post type not found or could not be deleted.' ) ); } }