Custom Form Approval Process

User
create a form with user fields and some custom post type fields, then save it to dummy created custom post type and give option to admin first approve it when admin approve i create user and listing in wordpress
Assistant
To create a form with user fields and custom post type (CPT) fields, save it to a dummy CPT, and give the admin the option to approve it before creating the user and listing, you can follow these steps in WordPress:

### Step 1: Register the Custom Post Type
First, register your custom post type (CPT) where the form data will be saved.

function register_custom_post_type() {
    $args = array(
        'labels' => array(
            'name' => 'Dummy Listings',
            'singular_name' => 'Dummy Listing',
        ),
        'public' => true,
        'has_archive' => true,
        'supports' => array('title', 'editor', 'custom-fields'),
        'show_in_rest' => true,
    );
    register_post_type('dummy_listing', $args);
}
add_action('init', 'register_custom_post_type');

### Step 2: Create the Form
Now create a form that collects both user information and CPT fields. This form can be placed in a template or a shortcode.

function create_custom_form() {
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        // Capture form data and save as CPT
        $user_data = $_POST['user_data'];
        $listing_data = $_POST['listing_data'];
        
        // Save the listing to the custom post type
        $post_data = array(
            'post_title'   => sanitize_text_field($listing_data['title']),
            'post_content' => sanitize_textarea_field($listing_data['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending', // Set to pending for approval
        );
        $post_id = wp_insert_post($post_data);
        
        // Save user data for later user creation
        update_post_meta($post_id, 'user_data', $user_data);
    }

    // Form HTML
    ?>
    <form method="POST">
        <h3>User Information</h3>
        <input type="text" name="user_data[name]" placeholder="Full Name" required />
        <input type="email" name="user_data[email]" placeholder="Email" required />
        
        <h3>Listing Information</h3>
        <input type="text" name="listing_data[title]" placeholder="Listing Title" required />
        <textarea name="listing_data[content]" placeholder="Listing Description"></textarea>
        
        <input type="submit" value="Submit" />
    </form>
    <?php
}

You can display this form using a shortcode or directly in a template.

### Step 3: Approve the Listing
Add a meta box or a custom admin page for approving the listing. Admins will need the option to approve the listing before the user is created.

function add_approve_meta_box() {
    add_meta_box(
        'approve_listing',
        'Approve Listing',
        'approve_listing_callback',
        'dummy_listing',
        'side',
        'high'
    );
}
add_action('add_meta_boxes', 'add_approve_meta_box');

function approve_listing_callback($post) {
    $user_data = get_post_meta($post->ID, 'user_data', true);
    ?>
    <h4>User Information</h4>
    <p>Name: <?php echo esc_html($user_data['name']); ?></p>
    <p>Email: <?php echo esc_html($user_data['email']); ?></p>

    <h4>Listing Information</h4>
    <p>Title: <?php echo esc_html(get_the_title($post->ID)); ?></p>
    <p>Description: <?php echo esc_html(get_the_content($post->ID)); ?></p>

    <form method="POST">
        <input type="submit" name="approve_listing" value="Approve" />
    </form>
    <?php
}

function handle_approve_listing() {
    if (isset($_POST['approve_listing'])) {
        $post_id = get_the_ID();
        
        // Approve listing (change status to publish)
        wp_update_post(array(
            'ID' => $post_id,
            'post_status' => 'publish',
        ));
        
        // Get user data
        $user_data = get_post_meta($post_id, 'user_data', true);
        
        // Create the user
        $user_id = wp_create_user($user_data['email'], wp_generate_password(), $user_data['email']);
        wp_update_user(array(
            'ID' => $user_id,
            'first_name' => $user_data['name'],
        ));
        
        // Save user ID as post meta
        update_post_meta($post_id, 'user_id', $user_id);
    }
}
add_action('save_post', 'handle_approve_listing');

### Explanation:
– **Custom Post Type**: `dummy_listing` is the CPT where listings are saved.
– **Form**: Collects user data (name, email) and listing data (title, content).
– **Admin Approval**: The admin can approve the listing by changing the status to `publish` in the meta box.
– **User Creation**: After approval, a new user is created with the provided email and name.

### Step 4: Display Approved Listings
To display the approved listings (created after admin approval), you can query and show them on the frontend.

function display_approved_listings() {
    $args = array(
        'post_type' => 'dummy_listing',
        'post_status' => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);
    
    if ($query->have_posts()) {
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
    } else {
        echo 'No listings available.';
    }
    wp_reset_postdata();
}

You can call this function on the desired page or use a shortcode.

### Step 5: Handle Form Submission
When the user submits the form, it will:
1. Create a new `dummy_listing` post in `pending` status.
2. Store user data as post meta.
3. The admin can approve it, which will create the user and make the listing live.

This is a basic workflow, and you can expand upon it depending on your exact requirements, such as adding more fields, email notifications, etc.

User
convert all code to shortcode
Assistant
Let's convert all the functionality into WordPress shortcodes for easier integration within your pages or posts. I'll break down the solution into multiple shortcodes:

1. **Shortcode for displaying the form**.
2. **Shortcode for displaying approved listings**.
3. **Functions to handle form submission and admin approval**.

### Step 1: Create Shortcode for the Form
Add the following code to your theme's `functions.php` file or a custom plugin.

// Register Custom Post Type
function register_custom_post_type() {
    $args = array(
        'labels' => array(
            'name' => 'Dummy Listings',
            'singular_name' => 'Dummy Listing',
        ),
        'public' => true,
        'has_archive' => true,
        'supports' => array('title', 'editor', 'custom-fields'),
        'show_in_rest' => true,
    );
    register_post_type('dummy_listing', $args);
}
add_action('init', 'register_custom_post_type');

// Shortcode for displaying the form
function custom_listing_form_shortcode() {
    ob_start();

    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
        // Capture form data
        $user_data = $_POST['user_data'];
        $listing_data = $_POST['listing_data'];

        // Save the listing to the custom post type
        $post_data = array(
            'post_title'   => sanitize_text_field($listing_data['title']),
            'post_content' => sanitize_textarea_field($listing_data['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending', // Set to pending for admin approval
        );
        $post_id = wp_insert_post($post_data);

        // Save user data as post meta
        if ($post_id) {
            update_post_meta($post_id, 'user_data', $user_data);
            echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
        }
    }

    // Form HTML
    ?>
    <form method="POST">
        <h3>User Information</h3>
        <input type="text" name="user_data[name]" placeholder="Full Name" required />
        <input type="email" name="user_data[email]" placeholder="Email" required />

        <h3>Listing Information</h3>
        <input type="text" name="listing_data[title]" placeholder="Listing Title" required />
        <textarea name="listing_data[content]" placeholder="Listing Description"></textarea>

        <input type="hidden" name="custom_listing_form" value="1" />
        <input type="submit" value="Submit" />
    </form>
    <?php

    return ob_get_clean();
}
add_shortcode('custom_listing_form', 'custom_listing_form_shortcode');

### Step 2: Shortcode for Displaying Approved Listings
Add this code to the same file:

// Shortcode for displaying approved listings
function display_approved_listings_shortcode() {
    ob_start();

    $args = array(
        'post_type' => 'dummy_listing',
        'post_status' => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('approved_listings', 'display_approved_listings_shortcode');

### Step 3: Admin Approval Functionality
Add a meta box to the admin edit screen for approving the submitted listings.

// Add meta box for approving listings
function add_approve_meta_box() {
    add_meta_box(
        'approve_listing',
        'Approve Listing',
        'approve_listing_callback',
        'dummy_listing',
        'side',
        'high'
    );
}
add_action('add_meta_boxes', 'add_approve_meta_box');

function approve_listing_callback($post) {
    $user_data = get_post_meta($post->ID, 'user_data', true);
    ?>
    <h4>User Information</h4>
    <p><strong>Name:</strong> <?php echo esc_html($user_data['name']); ?></p>
    <p><strong>Email:</strong> <?php echo esc_html($user_data['email']); ?></p>

    <form method="POST">
        <input type="submit" name="approve_listing" value="Approve" class="button button-primary" />
        <?php wp_nonce_field('approve_listing_nonce', 'approve_listing_nonce_field'); ?>
    </form>
    <?php
}

function handle_approve_listing($post_id) {
    // Check if the approve button was clicked
    if (isset($_POST['approve_listing']) && check_admin_referer('approve_listing_nonce', 'approve_listing_nonce_field')) {
        // Update post status to publish
        wp_update_post(array(
            'ID' => $post_id,
            'post_status' => 'publish',
        ));

        // Create the user
        $user_data = get_post_meta($post_id, 'user_data', true);
        if (!email_exists($user_data['email'])) {
            $user_id = wp_create_user($user_data['email'], wp_generate_password(), $user_data['email']);
            wp_update_user(array(
                'ID' => $user_id,
                'first_name' => $user_data['name'],
            ));
            // Link the created user to the listing
            update_post_meta($post_id, 'created_user_id', $user_id);
        }
    }
}
add_action('save_post_dummy_listing', 'handle_approve_listing');

### Step 4: How to Use the Shortcodes
1. **Add the form** to any page or post using:

[custom_listing_form]

2. **Display approved listings** on any page or post using:

[approved_listings]

### Explanation
– **Custom Form**: The shortcode `[custom_listing_form]` allows users to submit listings that include user data. These submissions are saved as posts in the `dummy_listing` CPT with a `pending` status.
– **Admin Approval**: The admin can review these submissions in the WordPress dashboard and approve them using a meta box. Approving a submission will create a WordPress user with the provided email.
– **Display Listings**: The shortcode `[approved_listings]` lists all the approved (published) listings on the frontend.

This solution provides a flexible way to handle user submissions, admin approvals, and frontend displays all through shortcodes.

User
all in one shortcode
Assistant
To handle everything within a single shortcode, including form submission, saving data to a custom post type, admin approval, and displaying approved listings, you can create a comprehensive solution in WordPress. Here's how you can achieve that.

### Step 1: Complete Solution with a Single Shortcode
Add the following code to your theme's `functions.php` file or a custom plugin.

// Register Custom Post Type
function register_custom_post_type() {
    $args = array(
        'labels' => array(
            'name'          => 'Dummy Listings',
        ),
        'public'        => true,
        'has_archive'   => true,
        'supports'      => array('title', 'editor', 'custom-fields'),
        'show_in_rest'  => true,
    );
    register_post_type('dummy_listing', $args);
}
add_action('init', 'register_custom_post_type');

// Main shortcode to handle form, submission, approval, and listing
function handle_custom_listing_shortcode() {
    ob_start();

    // Handle form submission
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
        $user_data = $_POST['user_data'];
        $listing_data = $_POST['listing_data'];

        // Save listing to custom post type with 'pending' status
        $post_data = array(
            'post_title'   => sanitize_text_field($listing_data['title']),
            'post_content' => sanitize_textarea_field($listing_data['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending',
        );
        $post_id = wp_insert_post($post_data);

        // Save user data as post meta
        if ($post_id) {
            update_post_meta($post_id, 'user_data', $user_data);
            echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
        }
    }

    // Form HTML
    ?>
    <form method="POST">
        <h3>User Information</h3>
        <input type="text" name="user_data[name]" placeholder="Full Name" required />
        <input type="email" name="user_data[email]" placeholder="Email" required />

        <h3>Listing Information</h3>
        <input type="text" name="listing_data[title]" placeholder="Listing Title" required />
        <textarea name="listing_data[content]" placeholder="Listing Description"></textarea>

        <input type="hidden" name="custom_listing_form" value="1" />
        <input type="submit" value="Submit" />
    </form>

    <hr>

    <h2>Approved Listings</h2>
    <?php

    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

// Add meta box for admin approval in the backend
function add_approve_meta_box() {
    add_meta_box(
        'approve_listing',
        'Approve Listing',
        'approve_listing_callback',
        'dummy_listing',
        'side',
        'high'
    );
}
add_action('add_meta_boxes', 'add_approve_meta_box');

function approve_listing_callback($post) {
    $user_data = get_post_meta($post->ID, 'user_data', true);
    ?>
    <h4>User Information</h4>
    <p><strong>Name:</strong> <?php echo esc_html($user_data['name']); ?></p>
    <p><strong>Email:</strong> <?php echo esc_html($user_data['email']); ?></p>

    <form method="POST">
        <input type="submit" name="approve_listing" value="Approve" class="button button-primary" />
        <?php wp_nonce_field('approve_listing_nonce', 'approve_listing_nonce_field'); ?>
    </form>
    <?php
}

// Handle approval in the backend
function handle_approve_listing($post_id) {
    if (isset($_POST['approve_listing']) && check_admin_referer('approve_listing_nonce', 'approve_listing_nonce_field')) {
        wp_update_post(array(
            'ID'          => $post_id,
            'post_status' => 'publish',
        ));

        // Create a WordPress user
        $user_data = get_post_meta($post_id, 'user_data', true);
        if ($user_data && !email_exists($user_data['email'])) {
            $user_id = wp_create_user($user_data['email'], wp_generate_password(), $user_data['email']);
            wp_update_user(array(
                'ID'         => $user_id,
                'first_name' => $user_data['name'],
            ));
            update_post_meta($post_id, 'created_user_id', $user_id);
        }
    }
}
add_action('save_post_dummy_listing', 'handle_approve_listing');

### Explanation
1. **Custom Post Type**: A CPT named `dummy_listing` is registered to save the form submissions.
2. **Single Shortcode**: The `[custom_listing]` shortcode does everything:
– Displays the form for users to submit their data.
– Saves the data to the CPT with a status of `pending`.
– Displays approved (published) listings.
3. **Admin Approval**:
– The admin can approve the submission from the post edit screen in the WordPress dashboard.
– Upon approval, a user is created using the submitted email.
4. **Meta Box**: The admin can approve the listing and create the user directly from the edit screen using a custom meta box.

### Step 2: How to Use the Shortcode
– Add the following shortcode to any page or post:

[custom_listing]

### Notes
– You can expand this solution with additional fields, error handling, and validation.
– Ensure that you have proper security measures like `sanitize_text_field()` and `sanitize_textarea_field()` for user inputs.
– You may want to style the form and listings using custom CSS for better presentation.

User
include seperate template for form including ob start
Assistant
To separate the form template into its own file and include it within the main shortcode function using `ob_start()`, you can follow these steps:

1. **Create a separate template file** for the form, which will contain the HTML structure.
2. **Include this template** in the shortcode function using `ob_start()` and `ob_get_clean()` to capture the output and render it where needed.

Here’s how to do it:

### Step 1: Create the Form Template File

1. Inside your theme folder, create a new directory called `templates` (or any other name you prefer) to store the form template.
2. Create a new file within this folder named `custom-listing-form.php`.

In `custom-listing-form.php`, add the HTML for the form:

<?php if (!defined('ABSPATH')) exit; // Exit if accessed directly ?>

<form method="POST">
    <h3>User Information</h3>
    <input type="text" name="user_data[name]" placeholder="Full Name" required />
    <input type="email" name="user_data[email]" placeholder="Email" required />

    <h3>Listing Information</h3>
    <input type="text" name="listing_data[title]" placeholder="Listing Title" required />
    <textarea name="listing_data[content]" placeholder="Listing Description"></textarea>

    <input type="hidden" name="custom_listing_form" value="1" />
    <input type="submit" value="Submit" />
</form>

### Step 2: Update the Shortcode to Include the Template

In your `functions.php` file or custom plugin file, update the shortcode function to load and display the form template. The updated function will look like this:

// Register Custom Post Type
function register_custom_post_type() {
    $args = array(
        'labels' => array(
            'name'          => 'Dummy Listings',
        ),
        'public'        => true,
        'has_archive'   => true,
        'supports'      => array('title', 'editor', 'custom-fields'),
        'show_in_rest'  => true,
    );
    register_post_type('dummy_listing', $args);
}
add_action('init', 'register_custom_post_type');

// Main shortcode to handle form, submission, approval, and listing
function handle_custom_listing_shortcode() {
    ob_start();

    // Handle form submission
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
        $user_data = $_POST['user_data'];
        $listing_data = $_POST['listing_data'];

        // Save listing to custom post type with 'pending' status
        $post_data = array(
            'post_title'   => sanitize_text_field($listing_data['title']),
            'post_content' => sanitize_textarea_field($listing_data['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending',
        );
        $post_id = wp_insert_post($post_data);

        // Save user data as post meta
        if ($post_id) {
            update_post_meta($post_id, 'user_data', $user_data);
            echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
        }
    }

    // Include the form template using ob_start
    $template_path = get_template_directory() . '/templates/custom-listing-form.php';
    if (file_exists($template_path)) {
        include $template_path;
    }

    echo '<hr><h2>Approved Listings</h2>';

    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

### Explanation of the Changes
– **Template Path**: The path to the `custom-listing-form.php` template is set using `get_template_directory() . '/templates/custom-listing-form.php'`.
– **Conditional Check**: The code checks if the template file exists before including it, ensuring the function doesn’t break if the file is missing.
– **ob_start() and ob_get_clean()**: These functions capture all output within the shortcode function, allowing the entire form, thank-you message, and listing output to be returned as a string.

### Usage
– Use `[custom_listing]` to display the form and approved listings on any page or post.

Assistant
To include a separate template for the form using `ob_start()` while still handling everything within a single shortcode, you can follow this approach:

### Step 1: Create a Template File for the Form
First, create a separate PHP file for the form. Save this file as `custom-listing-form.php` in your theme's folder (e.g., `wp-content/themes/your-theme`).

#### `custom-listing-form.php`

<?php
if (!defined('ABSPATH')) exit; // Exit if accessed directly

// Start output buffering
ob_start();
?>

<form method="POST">
    <h3>User Information</h3>
    <input type="text" name="user_data[name]" placeholder="Full Name" required />
    <input type="email" name="user_data[email]" placeholder="Email" required />

    <h3>Listing Information</h3>
    <input type="text" name="listing_data[title]" placeholder="Listing Title" required />
    <textarea name="listing_data[content]" placeholder="Listing Description"></textarea>

    <input type="hidden" name="custom_listing_form" value="1" />
    <input type="submit" value="Submit" />
</form>

<?php
// Return the buffered content
return ob_get_clean();
?>

### Step 2: Update the Shortcode in `functions.php`
Next, update your shortcode function to include this form template using `include()`.

#### Updated Code in `functions.php`

// Register Custom Post Type
function register_custom_post_type() {
    $args = array(
        'labels' => array(
            'name'          => 'Dummy Listings',
        ),
        'public'        => true,
        'has_archive'   => true,
        'supports'      => array('title', 'editor', 'custom-fields'),
        'show_in_rest'  => true,
    );
    register_post_type('dummy_listing', $args);
}
add_action('init', 'register_custom_post_type');

// Main shortcode to handle form, submission, approval, and listing
function handle_custom_listing_shortcode() {
    ob_start();

    // Handle form submission
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
        $user_data = $_POST['user_data'];
        $listing_data = $_POST['listing_data'];

        // Save listing to custom post type with 'pending' status
        $post_data = array(
            'post_title'   => sanitize_text_field($listing_data['title']),
            'post_content' => sanitize_textarea_field($listing_data['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending',
        );
        $post_id = wp_insert_post($post_data);

        // Save user data as post meta
        if ($post_id) {
            update_post_meta($post_id, 'user_data', $user_data);
            echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
        }
    }

    // Include the form template
    $form_template = get_template_directory() . '/custom-listing-form.php';
    if (file_exists($form_template)) {
        echo include($form_template);
    } else {
        echo '<p>Form template not found.</p>';
    }

    echo '<hr><h2>Approved Listings</h2>';

    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

// Add meta box for admin approval in the backend
function add_approve_meta_box() {
    add_meta_box(
        'approve_listing',
        'Approve Listing',
        'approve_listing_callback',
        'dummy_listing',
        'side',
        'high'
    );
}
add_action('add_meta_boxes', 'add_approve_meta_box');

function approve_listing_callback($post) {
    $user_data = get_post_meta($post->ID, 'user_data', true);
    ?>
    <h4>User Information</h4>
    <p><strong>Name:</strong> <?php echo esc_html($user_data['name']); ?></p>
    <p><strong>Email:</strong> <?php echo esc_html($user_data['email']); ?></p>

    <form method="POST">
        <input type="submit" name="approve_listing" value="Approve" class="button button-primary" />
        <?php wp_nonce_field('approve_listing_nonce', 'approve_listing_nonce_field'); ?>
    </form>
    <?php
}

// Handle approval in the backend
function handle_approve_listing($post_id) {
    if (isset($_POST['approve_listing']) && check_admin_referer('approve_listing_nonce', 'approve_listing_nonce_field')) {
        wp_update_post(array(
            'ID'          => $post_id,
            'post_status' => 'publish',
        ));

        // Create a WordPress user
        $user_data = get_post_meta($post_id, 'user_data', true);
        if ($user_data && !email_exists($user_data['email'])) {
            $user_id = wp_create_user($user_data['email'], wp_generate_password(), $user_data['email']);
            wp_update_user(array(
                'ID'         => $user_id,
                'first_name' => $user_data['name'],
            ));
            update_post_meta($post_id, 'created_user_id', $user_id);
        }
    }
}
add_action('save_post_dummy_listing', 'handle_approve_listing');

### Explanation
1. **Separate Template**: The form is now in `custom-listing-form.php`. This file is included in the main shortcode function using `include()`.
2. **Single Shortcode**: The `[custom_listing]` shortcode handles everything: form display, submission, saving data to a custom post type, and displaying approved listings.
3. **Admin Approval**: Admins can approve listings from the backend, which will automatically create a WordPress user.

### Step 3: How to Use the Shortcode
Add the following shortcode to any page or post:

[custom_listing]

This solution maintains the flexibility of using a separate template for the form while keeping all the functionality within a single shortcode.

User
add seperate short code for approve
Assistant
To create a separate shortcode for admin approval, we can add another shortcode that only admins can use to approve listings. This way, the main form and listing functionality are separated from the admin approval process.

### Step 1: Update `functions.php` with Two Shortcodes
We'll have:
– **Shortcode 1**: `[custom_listing]` – For the user form submission and displaying approved listings.
– **Shortcode 2**: `[approve_listing]` – For admin approval of pending listings.

Here's the updated code:

#### Updated Code in `functions.php`

// Register Custom Post Type
function register_custom_post_type() {
    $args = array(
        'labels' => array(
            'name'          => 'Dummy Listings',
        ),
        'public'        => true,
        'has_archive'   => true,
        'supports'      => array('title', 'editor', 'custom-fields'),
        'show_in_rest'  => true,
    );
    register_post_type('dummy_listing', $args);
}
add_action('init', 'register_custom_post_type');

// Shortcode 1: User Form Submission and Display Listings
function handle_custom_listing_shortcode() {
    ob_start();

    // Handle form submission
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
        $user_data = $_POST['user_data'];
        $listing_data = $_POST['listing_data'];

        // Save listing to custom post type with 'pending' status
        $post_data = array(
            'post_title'   => sanitize_text_field($listing_data['title']),
            'post_content' => sanitize_textarea_field($listing_data['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending',
        );
        $post_id = wp_insert_post($post_data);

        // Save user data as post meta
        if ($post_id) {
            update_post_meta($post_id, 'user_data', $user_data);
            echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
        }
    }

    // Include the form template
    $form_template = get_template_directory() . '/custom-listing-form.php';
    if (file_exists($form_template)) {
        echo include($form_template);
    } else {
        echo '<p>Form template not found.</p>';
    }

    echo '<hr><h2>Approved Listings</h2>';

    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

// Shortcode 2: Admin Approval of Listings
function handle_admin_approval_shortcode() {
    if (!current_user_can('manage_options')) {
        return '<p>You do not have permission to approve listings.</p>';
    }

    ob_start();

    // Handle approval submission
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['approve_listing']) && check_admin_referer('approve_listing_nonce', 'approve_listing_nonce_field')) {
        $post_id = intval($_POST['post_id']);
        wp_update_post(array(
            'ID'          => $post_id,
            'post_status' => 'publish',
        ));

        // Create a WordPress user if not exists
        $user_data = get_post_meta($post_id, 'user_data', true);
        if ($user_data && !email_exists($user_data['email'])) {
            $user_id = wp_create_user($user_data['email'], wp_generate_password(), $user_data['email']);
            wp_update_user(array(
                'ID'         => $user_id,
                'first_name' => $user_data['name'],
            ));
            update_post_meta($post_id, 'created_user_id', $user_id);
        }
        echo '<p>Listing approved and user created.</p>';
    }

    // Display pending listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'pending',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<form method="POST">';
        while ($query->have_posts()) {
            $query->the_post();
            $post_id = get_the_ID();
            $user_data = get_post_meta($post_id, 'user_data', true);
            ?>
            <div class="pending-listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
                <p><strong>Name:</strong> <?php echo esc_html($user_data['name']); ?></p>
                <p><strong>Email:</strong> <?php echo esc_html($user_data['email']); ?></p>

                <input type="hidden" name="post_id" value="<?php echo esc_attr($post_id); ?>" />
                <?php wp_nonce_field('approve_listing_nonce', 'approve_listing_nonce_field'); ?>
                <input type="submit" name="approve_listing" value="Approve" class="button button-primary" />
            </div>
            <hr>
            <?php
        }
        echo '</form>';
    } else {
        echo '<p>No pending listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('approve_listing', 'handle_admin_approval_shortcode');

### Explanation
1. **Custom Post Type**:
– The `dummy_listing` custom post type is used to save form submissions.

2. **Shortcode 1: `[custom_listing]`**:
– Displays a form for users to submit their listings.
– Saves the data to the custom post type with a status of `pending`.
– Displays approved listings.

3. **Shortcode 2: `[approve_listing]`**:
– Only accessible by admin users (`manage_options` capability).
– Displays a list of pending listings.
– Allows the admin to approve a listing and create a user based on the submitted email.
– Uses a nonce for security during approval.

### Step 2: How to Use the Shortcodes
– **User Form & Approved Listings**: Add this shortcode to any page or post:

[custom_listing]

– **Admin Approval**: Add this shortcode to an admin-only page or post:

[approve_listing]

This approach keeps the form, user submissions, and admin approval processes separate while allowing you to use them independently.

User
store $_POST in serliaze
Assistant
To store the `$_POST` data in a serialized format, you can use PHP's `serialize()` function. This will allow you to store the data as a string that can later be unserialized when you need to retrieve and use it. In this case, you'll serialize the `$_POST` data before saving it into the WordPress custom post type or post meta.

Here’s how you can modify the code to store the `$_POST` data in a serialized format:

### Step 1: Serialize and Store Data in the Custom Post

In the form submission handler, serialize the `$_POST` data before saving it into the custom post type.

#### Updated `handle_custom_listing_shortcode()` in `functions.php`

// Shortcode 1: User Form Submission and Display Listings
function handle_custom_listing_shortcode() {
    ob_start();

    // Handle form submission
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
        // Serialize the entire $_POST data
        $serialized_post_data = serialize($_POST);

        // Save listing to custom post type with 'pending' status
        $post_data = array(
            'post_title'   => sanitize_text_field($_POST['listing_data']['title']),
            'post_content' => sanitize_textarea_field($_POST['listing_data']['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending',
        );
        $post_id = wp_insert_post($post_data);

        // Store serialized POST data as post meta
        if ($post_id) {
            update_post_meta($post_id, 'serialized_post_data', $serialized_post_data);
            echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
        }
    }

    // Include the form template
    $form_template = get_template_directory() . '/custom-listing-form.php';
    if (file_exists($form_template)) {
        echo include($form_template);
    } else {
        echo '<p>Form template not found.</p>';
    }

    echo '<hr><h2>Approved Listings</h2>';

    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

### Step 2: Admin Approval and Unserialize Data

When the admin approves the listing, you can retrieve the serialized data from the post meta and unserialize it to access the original `$_POST` data.

#### Updated Admin Approval Function

// Shortcode 2: Admin Approval of Listings
function handle_admin_approval_shortcode() {
    if (!current_user_can('manage_options')) {
        return '<p>You do not have permission to approve listings.</p>';
    }

    ob_start();

    // Handle approval submission
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['approve_listing']) && check_admin_referer('approve_listing_nonce', 'approve_listing_nonce_field')) {
        $post_id = intval($_POST['post_id']);
        wp_update_post(array(
            'ID'          => $post_id,
            'post_status' => 'publish',
        ));

        // Retrieve and unserialize the POST data
        $serialized_post_data = get_post_meta($post_id, 'serialized_post_data', true);
        if ($serialized_post_data) {
            $post_data = unserialize($serialized_post_data);

            // You can now access the original $_POST data
            $user_data = $post_data['user_data'];
            $listing_data = $post_data['listing_data'];

            // Create a WordPress user if not exists
            if ($user_data && !email_exists($user_data['email'])) {
                $user_id = wp_create_user($user_data['email'], wp_generate_password(), $user_data['email']);
                wp_update_user(array(
                    'ID'         => $user_id,
                    'first_name' => $user_data['name'],
                ));
                update_post_meta($post_id, 'created_user_id', $user_id);
            }
        }

        echo '<p>Listing approved and user created.</p>';
    }

    // Display pending listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'pending',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<form method="POST">';
        while ($query->have_posts()) {
            $query->the_post();
            $post_id = get_the_ID();
            $user_data = get_post_meta($post_id, 'user_data', true);
            ?>
            <div class="pending-listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
                <p><strong>Name:</strong> <?php echo esc_html($user_data['name']); ?></p>
                <p><strong>Email:</strong> <?php echo esc_html($user_data['email']); ?></p>

                <input type="hidden" name="post_id" value="<?php echo esc_attr($post_id); ?>" />
                <?php wp_nonce_field('approve_listing_nonce', 'approve_listing_nonce_field'); ?>
                <input type="submit" name="approve_listing" value="Approve" class="button button-primary" />
            </div>
            <hr>
            <?php
        }
        echo '</form>';
    } else {
        echo '<p>No pending listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('approve_listing', 'handle_admin_approval_shortcode');

### Explanation of Changes:
1. **Serialize Data**:
– In the `handle_custom_listing_shortcode` function, the entire `$_POST` data is serialized using `serialize($_POST)` and stored in the `serialized_post_data` custom field for the custom post type (`dummy_listing`).

2. **Unserialize Data**:
– When the admin approves the listing in `handle_admin_approval_shortcode`, the serialized `$_POST` data is retrieved using `get_post_meta()` and then unserialized using `unserialize()` to access the original user data and listing data.

### Step 3: How to Use the Shortcodes
– **User Form & Approved Listings**: Add this shortcode to any page or post:

[custom_listing]

– **Admin Approval**: Add this shortcode to an admin-only page or post:

[approve_listing]

This solution now stores the form data in a serialized format, which allows it to be saved as a string and later unserialized for further processing.

User
after form submission redirect to same page by adding parameter
Assistant
To redirect the user to the same page after the form submission with a parameter in the URL, you can use PHP's `wp_redirect()` function. This function allows you to send an HTTP header to redirect to a different URL, and you can append query parameters to the URL during the redirect.

Here's how you can modify the form submission logic to redirect the user to the same page with a query parameter indicating successful submission.

### Step 1: Update the `handle_custom_listing_shortcode` function to Redirect

You need to update the code in `handle_custom_listing_shortcode` to redirect the user back to the same page after the form submission. You can do this by appending a query parameter like `?submission=success` to the URL.

#### Updated `handle_custom_listing_shortcode()` with Redirect

// Shortcode 1: User Form Submission and Display Listings
function handle_custom_listing_shortcode() {
    ob_start();

    // Handle form submission
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
        // Serialize the entire $_POST data
        $serialized_post_data = serialize($_POST);

        // Save listing to custom post type with 'pending' status
        $post_data = array(
            'post_title'   => sanitize_text_field($_POST['listing_data']['title']),
            'post_content' => sanitize_textarea_field($_POST['listing_data']['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending',
        );
        $post_id = wp_insert_post($post_data);

        // Store serialized POST data as post meta
        if ($post_id) {
            update_post_meta($post_id, 'serialized_post_data', $serialized_post_data);

            // Redirect to the same page with a success parameter
            $current_url = home_url(add_query_arg(array(), $_SERVER['REQUEST_URI']));
            $redirect_url = add_query_arg('submission', 'success', $current_url);
            wp_redirect($redirect_url);
            exit; // Always call exit after wp_redirect to prevent further code execution
        }
    }

    // Check if 'submission' query parameter is set and display a success message
    if (isset($_GET['submission']) && $_GET['submission'] === 'success') {
        echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
    }

    // Include the form template
    $form_template = get_template_directory() . '/custom-listing-form.php';
    if (file_exists($form_template)) {
        echo include($form_template);
    } else {
        echo '<p>Form template not found.</p>';
    }

    echo '<hr><h2>Approved Listings</h2>';

    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

### Explanation of the Changes:
1. **Redirect After Form Submission**:
– After the form is successfully submitted and the post is created, we get the current URL (`$_SERVER['REQUEST_URI']`), then append a query parameter `submission=success` to the URL.
– We use `wp_redirect()` to redirect the user back to the same page with the query parameter, indicating that the submission was successful.
– `exit` is called after `wp_redirect()` to ensure that no further code is executed after the redirect.

2. **Success Message**:
– If the `submission=success` query parameter is present in the URL, we display a success message above the form.

### Step 2: How the Form and URL Work
– After the form is submitted, the user will be redirected back to the same page with a URL like:

https://yourdomain.com/your-page/?submission=success

– The message `Thank you for your submission. Awaiting admin approval.` will appear at the top of the page if the query parameter `submission=success` is detected.

This will give the user feedback after they submit the form, and also maintain the state of the page with the success message and URL parameter.

User
pre fill form values when validation failed after submit
Assistant
To pre-fill the form values when validation fails and after a form submission, you need to keep the submitted values in `$_POST` so that they can be reused in the form fields. When validation fails, you can check the `$_POST` data and populate the form fields with those values.

Here's how you can modify the existing code to pre-fill the form fields after a failed submission:

### Step 1: Modify the `handle_custom_listing_shortcode` Function to Retain `$_POST` Data

You need to ensure that the form fields are populated with the `$_POST` values when the form is redisplayed due to validation failure.

#### Updated `handle_custom_listing_shortcode()`:

// Shortcode 1: User Form Submission and Display Listings
function handle_custom_listing_shortcode() {
    ob_start();

    // Handle form submission
    $listing_data = isset($_POST['listing_data']) ? $_POST['listing_data'] : array(); // Retain submitted data for pre-filling

    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
        // Validate form data (simple validation example)
        $errors = array();
        if (empty($listing_data['title'])) {
            $errors[] = 'Title is required.';
        }
        if (empty($listing_data['content'])) {
            $errors[] = 'Content is required.';
        }

        if (empty($errors)) {
            // Serialize the entire $_POST data
            $serialized_post_data = serialize($_POST);

            // Save listing to custom post type with 'pending' status
            $post_data = array(
                'post_title'   => sanitize_text_field($listing_data['title']),
                'post_content' => sanitize_textarea_field($listing_data['content']),
                'post_type'    => 'dummy_listing',
                'post_status'  => 'pending',
            );
            $post_id = wp_insert_post($post_data);

            // Store serialized POST data as post meta
            if ($post_id) {
                update_post_meta($post_id, 'serialized_post_data', $serialized_post_data);

                // Redirect to the same page with a success parameter
                $current_url = home_url(add_query_arg(array(), $_SERVER['REQUEST_URI']));
                $redirect_url = add_query_arg('submission', 'success', $current_url);
                wp_redirect($redirect_url);
                exit; // Always call exit after wp_redirect to prevent further code execution
            }
        }
    }

    // Check if 'submission' query parameter is set and display a success message
    if (isset($_GET['submission']) && $_GET['submission'] === 'success') {
        echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
    }

    // Display form
    ?>
    <form method="POST">
        <?php wp_nonce_field('custom_listing_form_nonce', 'custom_listing_form_nonce_field'); ?>

        <div>
            <label for="listing_title">Title:</label>
            <input type="text" id="listing_title" name="listing_data[title]" value="<?php echo esc_attr(isset($listing_data['title']) ? $listing_data['title'] : ''); ?>" required />
        </div>

        <div>
            <label for="listing_content">Content:</label>
            <textarea id="listing_content" name="listing_data[content]" required><?php echo esc_textarea(isset($listing_data['content']) ? $listing_data['content'] : ''); ?></textarea>
        </div>

        <?php
        // Display errors if validation fails
        if (!empty($errors)) {
            echo '<div class="form-errors"><ul>';
            foreach ($errors as $error) {
                echo '<li>' . esc_html($error) . '</li>';
            }
            echo '</ul></div>';
        }
        ?>

        <div>
            <input type="submit" name="custom_listing_form" value="Submit Listing" />
        </div>
    </form>

    <hr><h2>Approved Listings</h2>

    <?php
    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

### Explanation of Changes:
1. **Retain Submitted Data (`$listing_data`)**:
– The `$_POST['listing_data']` is retained in the `$listing_data` variable to pre-fill the form fields if the form is redisplayed after a failed submission.
– The form fields' `value` attributes are set to the corresponding values from `$listing_data`, ensuring that the user's previous input is retained.

2. **Form Validation**:
– The code checks if the required fields (`title` and `content`) are filled out. If either is empty, an error message is added to the `$errors` array.
– If no errors are found, the post is created and the user is redirected. Otherwise, the form is displayed again with the validation errors.

3. **Displaying Errors**:
– If there are validation errors, they are displayed in a list above the form.

### Step 2: How This Works
– When the form is submitted, the page is either:
1. **Redirected** if the submission is successful, and a success message is displayed.
2. **Re-rendered** with the user's previously entered values and any validation errors if there were issues with the submission.

– The user can see the previous form input, which makes correcting the form easier.

This approach will ensure the form is pre-filled with the data the user submitted, even if validation fails.

User
jQuery(document).ready(function($) {
// Your jQuery code here
console.log("jQuery is ready!");

$('.geodir-add-listing').on('click','.addmoreservice',function() {
$("#moreservicediv").append(`

+

`);
});
});
make remove function

Assistant
To create the remove function for dynamically added service fields, you can modify the `$('.removemoreservice')` click event to remove the corresponding service input when the user clicks the "-" button.

Here's the updated jQuery code with both the "add" and "remove" functionality:

jQuery(document).ready(function($) {
    // Your jQuery code here
    console.log("jQuery is ready!");

    // Add more services when the '+' button is clicked
    $('.geodir-add-listing').on('click', '.addmoreservice', function() {
        var newServiceId = Date.now(); // Generate a unique ID based on timestamp
        $("#moreservicediv").append(`
            <div id="service-${newServiceId}">
                <input type="text" name="services_offered_service_name[]" class="form-control">
                <a href="javascript:void(0)" class="addmoreservice">+</a>
                <a href="javascript:void(0)" class="removemoreservice">-</a>
            </div>
        `);
    });

    // Remove the service when the '-' button is clicked
    $("#moreservicediv").on('click', '.removemoreservice', function() {
        $(this).closest('div').remove(); // Remove the entire service input div
    });
});

### Explanation:
1. **Add Service**:
– When the user clicks the "+" button (`.addmoreservice`), a new input field with a unique ID is added to the `#moreservicediv` container. This allows users to add more services.

2. **Remove Service**:
– When the user clicks the "-" button (`.removemoreservice`), the closest “ containing that service input is removed using the `$(this).closest('div').remove()` method. This removes the service input and the corresponding buttons (`+` and `-`).

### Key Changes:
– The remove function listens for clicks on the `.removemoreservice` elements inside `#moreservicediv`, ensuring that only the specific input field and buttons are removed, not the entire form or other fields.

User
check before submit form if services_offered_service_name at least 3 has field
Assistant
To check before form submission if at least 3 service fields are filled out, you can use jQuery to validate the form before it is submitted. Specifically, you can count the non-empty input fields in the `services_offered_service_name[]` field array and show a validation message if there are fewer than 3 services.

Here’s how you can modify the code to implement this validation:

### Updated jQuery Code:

jQuery(document).ready(function($) {
    // Your jQuery code here
    console.log("jQuery is ready!");

    // Add more services when the '+' button is clicked
    $('.geodir-add-listing').on('click', '.addmoreservice', function() {
        var newServiceId = Date.now(); // Generate a unique ID based on timestamp
        $("#moreservicediv").append(`
            <div id="service-${newServiceId}">
                <input type="text" name="services_offered_service_name[]" class="form-control">
                <a href="javascript:void(0)" class="addmoreservice">+</a>
                <a href="javascript:void(0)" class="removemoreservice">-</a>
            </div>
        `);
    });

    // Remove the service when the '-' button is clicked
    $("#moreservicediv").on('click', '.removemoreservice', function() {
        $(this).closest('div').remove(); // Remove the entire service input div
    });

    // Before form submission, check if there are at least 3 services
    $('form').on('submit', function(e) {
        var serviceCount = 0;
        // Count the non-empty service input fields
        $('input[name="services_offered_service_name[]"]').each(function() {
            if ($(this).val().trim() !== '') {
                serviceCount++;
            }
        });

        // If fewer than 3 services are provided, show an alert and prevent form submission
        if (serviceCount < 3) {
            e.preventDefault(); // Prevent form submission
            alert('Please provide at least 3 services.');
        }
    });
});

### Explanation:
1. **Add Service**: The `+` button dynamically adds a new service field as before.
2. **Remove Service**: The `-` button removes the service field as before.
3. **Before Form Submission**:
– The `submit` event listener is attached to the form.
– It counts the number of non-empty `input[name="services_offered_service_name[]"]` fields.
– If fewer than 3 fields are filled out (i.e., their value is not empty), it triggers the `alert()` and prevents the form submission using `e.preventDefault()`.
– If there are 3 or more services, the form is allowed to submit as normal.

### How It Works:
– When the form is submitted, the script will check if at least 3 of the `services_offered_service_name[]` fields have values. If not, it will prevent the form from being submitted and show an alert telling the user to provide at least 3 services.

User
above short code form submission not submitting the form even says page not found
Assistant
If the form is redirecting to a "Page Not Found" error when submitted, this usually indicates that the form submission is being processed incorrectly or the form action URL is not set correctly. In WordPress, the form action should typically point to the current page or be left empty, which defaults to submitting to the same page.

Let’s update the form and submission handling to ensure it works correctly.

### Updated Shortcode with Form Handling:

1. **Ensure the Form Submits to the Current Page**: We'll use `action=""` in the form tag.
2. **Fix Form Submission Handling**: We'll use `admin_post_nopriv_` and `admin_post_` actions to handle the form submission properly.
3. **Add Nonce Field for Security**: We'll include a nonce field to protect against CSRF attacks.

### Updated Code:

#### Step 1: Update the Shortcode to Include a Form

// Shortcode 1: User Form Submission and Display Listings
function handle_custom_listing_shortcode() {
    ob_start();

    // Check if 'submission' query parameter is set and display a success message
    if (isset($_GET['submission']) && $_GET['submission'] === 'success') {
        echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
    }

    // Display form
    ?>
    <form method="POST" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
        <?php wp_nonce_field('custom_listing_form_nonce', 'custom_listing_form_nonce_field'); ?>
        <input type="hidden" name="action" value="handle_custom_listing_form">

        <div>
            <label for="listing_title">Title:</label>
            <input type="text" id="listing_title" name="listing_data[title]" value="<?php echo esc_attr($_POST['listing_data']['title'] ?? ''); ?>" required />
        </div>

        <div>
            <label for="listing_content">Content:</label>
            <textarea id="listing_content" name="listing_data[content]" required><?php echo esc_textarea($_POST['listing_data']['content'] ?? ''); ?></textarea>
        </div>

        <div id="moreservicediv">
            <label for="services">Services Offered:</label>
            <div id="service-1">
                <input type="text" name="services_offered_service_name[]" class="form-control">
                <a href="javascript:void(0)" class="addmoreservice">+</a>
            </div>
        </div>

        <div>
            <input type="submit" name="custom_listing_form" value="Submit Listing" />
        </div>
    </form>
    <hr>

    <h2>Approved Listings</h2>
    <?php
    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

#### Step 2: Add Form Submission Handling with `admin_post`

In your `functions.php` or a custom plugin file:

// Handle the form submission
function handle_custom_listing_form_submission() {
    // Check if the nonce is valid
    if (!isset($_POST['custom_listing_form_nonce_field']) || !wp_verify_nonce($_POST['custom_listing_form_nonce_field'], 'custom_listing_form_nonce')) {
        wp_die('Nonce verification failed');
    }

    // Validate and sanitize input
    $listing_data = $_POST['listing_data'] ?? [];
    $services = $_POST['services_offered_service_name'] ?? [];
    $errors = [];

    if (empty($listing_data['title'])) {
        $errors[] = 'Title is required.';
    }
    if (empty($listing_data['content'])) {
        $errors[] = 'Content is required.';
    }
    if (count(array_filter($services)) < 3) {
        $errors[] = 'Please provide at least 3 services.';
    }

    if (!empty($errors)) {
        // Redirect back to the form with errors
        $redirect_url = add_query_arg('submission', 'failed', wp_get_referer());
        wp_redirect($redirect_url);
        exit;
    }

    // Serialize data and save post
    $serialized_post_data = serialize($_POST);
    $post_data = [
        'post_title'   => sanitize_text_field($listing_data['title']),
        'post_content' => sanitize_textarea_field($listing_data['content']),
        'post_type'    => 'dummy_listing',
        'post_status'  => 'pending',
    ];

    $post_id = wp_insert_post($post_data);
    if ($post_id) {
        update_post_meta($post_id, 'serialized_post_data', $serialized_post_data);
        $redirect_url = add_query_arg('submission', 'success', wp_get_referer());
        wp_redirect($redirect_url);
        exit;
    }
}
add_action('admin_post_nopriv_handle_custom_listing_form', 'handle_custom_listing_form_submission');
add_action('admin_post_handle_custom_listing_form', 'handle_custom_listing_form_submission');

### Explanation:
1. **Form Handling**:
– The form submits to `admin-post.php` with a hidden action field.
– Uses `wp_nonce_field()` for security.

2. **Submission Handling**:
– Checks if the nonce is valid.
– Validates the required fields and counts the services.
– If validation fails, it redirects to the referring page with a `submission=failed` parameter.
– If successful, saves the post with a status of "pending" and redirects to the same page with `submission=success`.

3. **Redirection**:
– Redirects users to the same page with a query parameter indicating success or failure.

This code should ensure the form submits correctly and handles redirection without showing "Page Not Found" errors.

User
create user with role provider
Assistant
To create a user with a specific role (like "provider") upon form submission in WordPress, we need to extend the previous code by adding logic for user creation. Here's how you can achieve that:

1. **Update the form to include user-related fields (like email, password, etc.).**
2. **Modify the form handling function to create a user with the "provider" role.**

### Step 1: Update the Shortcode to Include User Fields

// Shortcode: User Form Submission and Display Listings
function handle_custom_listing_shortcode() {
    ob_start();

    // Check if 'submission' query parameter is set and display a success message
    if (isset($_GET['submission']) && $_GET['submission'] === 'success') {
        echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
    } elseif (isset($_GET['submission']) && $_GET['submission'] === 'failed') {
        echo '<p>There was an error with your submission. Please try again.</p>';
    }

    // Display the form
    ?>
    <form method="POST" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
        <?php wp_nonce_field('custom_listing_form_nonce', 'custom_listing_form_nonce_field'); ?>
        <input type="hidden" name="action" value="handle_custom_listing_form">

        <h3>Listing Details</h3>
        <div>
            <label for="listing_title">Title:</label>
            <input type="text" id="listing_title" name="listing_data[title]" value="<?php echo esc_attr($_POST['listing_data']['title'] ?? ''); ?>" required />
        </div>

        <div>
            <label for="listing_content">Content:</label>
            <textarea id="listing_content" name="listing_data[content]" required><?php echo esc_textarea($_POST['listing_data']['content'] ?? ''); ?></textarea>
        </div>

        <div id="moreservicediv">
            <label for="services">Services Offered:</label>
            <div id="service-1">
                <input type="text" name="services_offered_service_name[]" class="form-control" value="<?php echo esc_attr($_POST['services_offered_service_name'][0] ?? ''); ?>">
                <a href="javascript:void(0)" class="addmoreservice">+</a>
            </div>
        </div>

        <h3>User Details</h3>
        <div>
            <label for="user_email">Email:</label>
            <input type="email" id="user_email" name="user_data[email]" value="<?php echo esc_attr($_POST['user_data']['email'] ?? ''); ?>" required />
        </div>

        <div>
            <label for="user_password">Password:</label>
            <input type="password" id="user_password" name="user_data[password]" required />
        </div>

        <div>
            <input type="submit" name="custom_listing_form" value="Submit Listing" />
        </div>
    </form>
    <hr>

    <h2>Approved Listings</h2>
    <?php
    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

### Step 2: Handle Form Submission and User Creation

In your `functions.php` or a custom plugin file:

// Handle the form submission
function handle_custom_listing_form_submission() {
    // Check if the nonce is valid
    if (!isset($_POST['custom_listing_form_nonce_field']) || !wp_verify_nonce($_POST['custom_listing_form_nonce_field'], 'custom_listing_form_nonce')) {
        wp_die('Nonce verification failed');
    }

    $listing_data = $_POST['listing_data'] ?? [];
    $services = $_POST['services_offered_service_name'] ?? [];
    $user_data = $_POST['user_data'] ?? [];
    $errors = [];

    // Validate listing data
    if (empty($listing_data['title'])) {
        $errors[] = 'Title is required.';
    }
    if (empty($listing_data['content'])) {
        $errors[] = 'Content is required.';
    }
    if (count(array_filter($services)) < 3) {
        $errors[] = 'Please provide at least 3 services.';
    }

    // Validate user data
    if (empty($user_data['email']) || !is_email($user_data['email'])) {
        $errors[] = 'A valid email is required.';
    }
    if (empty($user_data['password'])) {
        $errors[] = 'Password is required.';
    }

    if (!empty($errors)) {
        $redirect_url = add_query_arg('submission', 'failed', wp_get_referer());
        wp_redirect($redirect_url);
        exit;
    }

    // Create a new user with the "provider" role
    $user_id = wp_create_user(
        sanitize_email($user_data['email']),
        sanitize_text_field($user_data['password']),
        sanitize_email($user_data['email'])
    );

    if (is_wp_error($user_id)) {
        wp_die('User creation failed: ' . $user_id->get_error_message());
    } else {
        // Assign the "provider" role to the new user
        $user = new WP_User($user_id);
        $user->set_role('provider');
    }

    // Serialize data and save post
    $serialized_post_data = serialize($_POST);
    $post_data = [
        'post_title'   => sanitize_text_field($listing_data['title']),
        'post_content' => sanitize_textarea_field($listing_data['content']),
        'post_type'    => 'dummy_listing',
        'post_status'  => 'pending',
        'post_author'  => $user_id,
    ];

    $post_id = wp_insert_post($post_data);
    if ($post_id) {
        update_post_meta($post_id, 'serialized_post_data', $serialized_post_data);
        $redirect_url = add_query_arg('submission', 'success', wp_get_referer());
        wp_redirect($redirect_url);
        exit;
    }
}
add_action('admin_post_nopriv_handle_custom_listing_form', 'handle_custom_listing_form_submission');
add_action('admin_post_handle_custom_listing_form', 'handle_custom_listing_form_submission');

### Explanation:
1. **User Fields**: The form now includes fields for email and password.
2. **User Creation**: The `handle_custom_listing_form_submission` function creates a new user with the "provider" role.
3. **Validation**:
– The form checks for at least 3 services.
– Ensures the email is valid and a password is provided.
4. **Redirection**: After successful submission, it redirects to the same page with `submission=success`. If there are errors, it redirects with `submission=failed`.

Make sure you have a "provider" role set up in your WordPress environment. If not, you can add one using:

add_role('provider', 'Provider', ['read' => true, 'edit_posts' => true]);

This solution should let you create users with the "provider" role upon form submission and save their listings as custom post types.

User
also add first and last name with address
Assistant
To add additional fields like **First Name**, **Last Name**, and **Address**, we need to extend both the form and the form handling code. We'll ensure that these details are saved with the newly created user and also stored in the custom post type.

Here's the complete solution with the additional fields integrated:

### Step 1: Update the Shortcode to Include New Fields

// Shortcode: User Form Submission and Display Listings
function handle_custom_listing_shortcode() {
    ob_start();

    // Check if 'submission' query parameter is set and display a success message
    if (isset($_GET['submission']) && $_GET['submission'] === 'success') {
        echo '<p>Thank you for your submission. Awaiting admin approval.</p>';
    } elseif (isset($_GET['submission']) && $_GET['submission'] === 'failed') {
        echo '<p>There was an error with your submission. Please try again.</p>';
    }

    // Display the form
    ?>
    <form method="POST" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
        <?php wp_nonce_field('custom_listing_form_nonce', 'custom_listing_form_nonce_field'); ?>
        <input type="hidden" name="action" value="handle_custom_listing_form">

        <h3>Listing Details</h3>
        <div>
            <label for="listing_title">Title:</label>
            <input type="text" id="listing_title" name="listing_data[title]" value="<?php echo esc_attr($_POST['listing_data']['title'] ?? ''); ?>" required />
        </div>

        <div>
            <label for="listing_content">Content:</label>
            <textarea id="listing_content" name="listing_data[content]" required><?php echo esc_textarea($_POST['listing_data']['content'] ?? ''); ?></textarea>
        </div>

        <div id="moreservicediv">
            <label for="services">Services Offered:</label>
            <div id="service-1">
                <input type="text" name="services_offered_service_name[]" class="form-control" value="<?php echo esc_attr($_POST['services_offered_service_name'][0] ?? ''); ?>">
                <a href="javascript:void(0)" class="addmoreservice">+</a>
            </div>
        </div>

        <h3>User Details</h3>
        <div>
            <label for="first_name">First Name:</label>
            <input type="text" id="first_name" name="user_data[first_name]" value="<?php echo esc_attr($_POST['user_data']['first_name'] ?? ''); ?>" required />
        </div>

        <div>
            <label for="last_name">Last Name:</label>
            <input type="text" id="last_name" name="user_data[last_name]" value="<?php echo esc_attr($_POST['user_data']['last_name'] ?? ''); ?>" required />
        </div>

        <div>
            <label for="user_email">Email:</label>
            <input type="email" id="user_email" name="user_data[email]" value="<?php echo esc_attr($_POST['user_data']['email'] ?? ''); ?>" required />
        </div>

        <div>
            <label for="user_password">Password:</label>
            <input type="password" id="user_password" name="user_data[password]" required />
        </div>

        <div>
            <label for="address">Address:</label>
            <textarea id="address" name="user_data[address]" required><?php echo esc_textarea($_POST['user_data']['address'] ?? ''); ?></textarea>
        </div>

        <div>
            <input type="submit" name="custom_listing_form" value="Submit Listing" />
        </div>
    </form>
    <hr>

    <h2>Approved Listings</h2>
    <?php
    // Display approved listings
    $args = array(
        'post_type'      => 'dummy_listing',
        'post_status'    => 'publish',
        'posts_per_page' => 10,
    );
    $query = new WP_Query($args);

    if ($query->have_posts()) {
        echo '<div class="approved-listings">';
        while ($query->have_posts()) {
            $query->the_post();
            ?>
            <div class="listing">
                <h3><?php the_title(); ?></h3>
                <p><?php the_content(); ?></p>
            </div>
            <?php
        }
        echo '</div>';
    } else {
        echo '<p>No approved listings found.</p>';
    }
    wp_reset_postdata();

    return ob_get_clean();
}
add_shortcode('custom_listing', 'handle_custom_listing_shortcode');

### Step 2: Update Form Submission Handling

In your `functions.php` or a custom plugin file:

// Handle the form submission
function handle_custom_listing_form_submission() {
    // Check if the nonce is valid
    if (!isset($_POST['custom_listing_form_nonce_field']) || !wp_verify_nonce($_POST['custom_listing_form_nonce_field'], 'custom_listing_form_nonce')) {
        wp_die('Nonce verification failed');
    }

    $listing_data = $_POST['listing_data'] ?? [];
    $services = $_POST['services_offered_service_name'] ?? [];
    $user_data = $_POST['user_data'] ?? [];
    $errors = [];

    // Validate listing data
    if (empty($listing_data['title'])) {
        $errors[] = 'Title is required.';
    }
    if (empty($listing_data['content'])) {
        $errors[] = 'Content is required.';
    }
    if (count(array_filter($services)) < 3) {
        $errors[] = 'Please provide at least 3 services.';
    }

    // Validate user data
    if (empty($user_data['first_name'])) {
        $errors[] = 'First name is required.';
    }
    if (empty($user_data['last_name'])) {
        $errors[] = 'Last name is required.';
    }
    if (empty($user_data['email']) || !is_email($user_data['email'])) {
        $errors[] = 'A valid email is required.';
    }
    if (empty($user_data['password'])) {
        $errors[] = 'Password is required.';
    }
    if (empty($user_data['address'])) {
        $errors[] = 'Address is required.';
    }

    if (!empty($errors)) {
        $redirect_url = add_query_arg('submission', 'failed', wp_get_referer());
        wp_redirect($redirect_url);
        exit;
    }

    // Create a new user with the "provider" role
    $user_id = wp_create_user(
        sanitize_email($user_data['email']),
        sanitize_text_field($user_data['password']),
        sanitize_email($user_data['email'])
    );

    if (is_wp_error($user_id)) {
        wp_die('User creation failed: ' . $user_id->get_error_message());
    } else {
        // Assign the "provider" role to the new user
        $user = new WP_User($user_id);
        $user->set_role('provider');

        // Update user meta data
        update_user_meta($user_id, 'first_name', sanitize_text_field($user_data['first_name']));
        update_user_meta($user_id, 'last_name', sanitize_text_field($user_data['last_name']));
        update_user_meta($user_id, 'address', sanitize_textarea_field($user_data['address']));
    }

    // Serialize data and save post
    $serialized_post_data = serialize($_POST);
    $post_data = [
        'post_title'   => sanitize_text_field($listing_data['title']),
        'post_content' => sanitize_textarea_field($listing_data['content']),
        'post_type'    => 'dummy_listing',
        'post_status'  => 'pending',
        'post_author'  => $user_id,
    ];

    $post_id = wp_insert_post($post_data);
    if ($post_id) {
        update_post_meta($post_id, 'serialized_post_data', $serialized_post_data);
        $redirect_url = add_query_arg('submission', 'success', wp_get_referer());
        wp_redirect($redirect_url);
        exit;
    }
}
add_action('admin_post_nopriv_handle_custom_listing_form', 'handle_custom_listing_form_submission');
add_action('admin_post_handle_custom_listing_form', 'handle_custom_listing_form_submission');

### Explanation:
1. **Added New Fields**: Included fields for **First Name**, **Last Name**, and **Address** in the form.
2. **Extended Validation**: Updated validation to check for these new fields.
3. **User Metadata**: After creating the user, the code updates their metadata with the first name, last name, and address.
4. **Redirection and Error Handling**: Handles both success and error cases, redirecting the user accordingly.

This solution will now create a new user with the role "provider" and save the custom post with all the submitted details.

User
assing cateogries to custom post type "gd_placecategory" when creating post
Assistant
To assign categories to a custom post type (like `gd_placecategory`) when creating a post, you can use the `wp_set_object_terms()` function. This function allows you to assign one or more terms (categories, tags, or custom taxonomies) to a post.

Here's how you can update your code to assign categories to the custom post type when creating a new post.

### Step 1: Register the Custom Taxonomy (if not already registered)

First, ensure that your custom taxonomy `gd_placecategory` is registered for your custom post type (e.g., `dummy_listing`). This should be done in your theme's `functions.php` or a custom plugin.

// Register Custom Taxonomy
function register_gd_placecategory_taxonomy() {
    $labels = array(
        'name'              => 'Place Categories',
        'singular_name'     => 'Place Category',
        'search_items'      => 'Search Place Categories',
        'all_items'         => 'All Place Categories',
        'parent_item'       => 'Parent Place Category',
        'parent_item_colon' => 'Parent Place Category:',
        'edit_item'         => 'Edit Place Category',
        'update_item'       => 'Update Place Category',
        'add_new_item'      => 'Add New Place Category',
        'new_item_name'     => 'New Place Category Name',
        'menu_name'         => 'Place Categories',
    );

    $args = array(
        'labels'            => $labels,
        'hierarchical'      => true,
        'public'            => true,
        'show_ui'           => true,
        'show_admin_column' => true,
        'query_var'         => true,
        'rewrite'           => array('slug' => 'gd-placecategory'),
    );

    register_taxonomy('gd_placecategory', 'dummy_listing', $args);
}
add_action('init', 'register_gd_placecategory_taxonomy');

### Step 2: Assign Categories When Creating a New Post

When you create a new post programmatically (e.g., in your form submission handler), you can assign categories from the `gd_placecategory` taxonomy using `wp_set_object_terms()`.

#### Updated Code to Assign Categories:

// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
    $listing_data = isset($_POST['listing_data']) ? $_POST['listing_data'] : array(); // Retain submitted data

    // Validate form data (simple validation example)
    $errors = array();
    if (empty($listing_data['title'])) {
        $errors[] = 'Title is required.';
    }
    if (empty($listing_data['content'])) {
        $errors[] = 'Content is required.';
    }

    if (empty($errors)) {
        // Serialize the entire $_POST data
        $serialized_post_data = serialize($_POST);

        // Save listing to custom post type with 'pending' status
        $post_data = array(
            'post_title'   => sanitize_text_field($listing_data['title']),
            'post_content' => sanitize_textarea_field($listing_data['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending',
        );
        $post_id = wp_insert_post($post_data);

        // Assign categories to the post (change category IDs as needed)
        if ($post_id) {
            // Check if categories are set in the form and assign them
            if (!empty($_POST['categories'])) {
                $categories = array_map('intval', $_POST['categories']); // Ensure category IDs are integers
                wp_set_object_terms($post_id, $categories, 'gd_placecategory');
            }

            // Store serialized POST data as post meta
            update_post_meta($post_id, 'serialized_post_data', $serialized_post_data);

            // Redirect to the same page with a success parameter
            $current_url = home_url(add_query_arg(array(), $_SERVER['REQUEST_URI']));
            $redirect_url = add_query_arg('submission', 'success', $current_url);
            wp_redirect($redirect_url);
            exit; // Always call exit after wp_redirect to prevent further code execution
        }
    }
}

### Explanation:
1. **Registering the Custom Taxonomy**:
– The `register_gd_placecategory_taxonomy()` function registers a hierarchical taxonomy (`gd_placecategory`) for the custom post type `dummy_listing`.

2. **Assigning Categories to a Post**:
– In the form submission handler, after creating the post using `wp_insert_post()`, we assign categories to the new post using `wp_set_object_terms()`.
– The categories are expected to be passed from the form via `$_POST['categories']`.
– The `array_map('intval', …)` function ensures that the category IDs are integers, preventing any unexpected input.

### Step 3: Update the HTML Form to Include Category Selection

Make sure your form includes a way for users to select categories:

<form method="POST">
    <label for="listing_title">Title:</label>
    <input type="text" name="listing_data[title]" required><br>

    <label for="listing_content">Content:</label>
    <textarea name="listing_data[content]" required></textarea><br>

    <label for="categories">Select Categories:</label>
    <select name="categories[]" multiple>
        <?php
        // Fetch categories from 'gd_placecategory'
        $categories = get_terms(array(
            'taxonomy'   => 'gd_placecategory',
            'hide_empty' => false,
        ));
        foreach ($categories as $category) {
            echo '<option value="' . esc_attr($category->term_id) . '">' . esc_html($category->name) . '</option>';
        }
        ?>
    </select><br>

    <input type="submit" name="custom_listing_form" value="Submit Listing">
</form>

### How It Works:
– **Register the Taxonomy**: Ensures `gd_placecategory` is available for your custom post type.
– **Assign Categories**: Uses `wp_set_object_terms()` to assign selected categories when creating a post.
– **Form**: Allows users to select multiple categories before submitting.

This approach ensures your posts are categorized correctly based on user input when creating custom post types in WordPress.

User
above are creating new catoegories
Assistant
If the code is creating new categories instead of assigning existing ones, it’s likely due to how the `wp_set_object_terms()` function is handling the input. By default, if a term (category) doesn't exist, WordPress will create it. To ensure that only existing categories are assigned, we need to fetch the existing category IDs first and use those.

Here’s how you can modify the code to assign only existing categories without creating new ones:

### Updated Code to Assign Existing Categories:

// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
    $listing_data = isset($_POST['listing_data']) ? $_POST['listing_data'] : array(); // Retain submitted data

    // Validate form data (simple validation example)
    $errors = array();
    if (empty($listing_data['title'])) {
        $errors[] = 'Title is required.';
    }
    if (empty($listing_data['content'])) {
        $errors[] = 'Content is required.';
    }

    if (empty($errors)) {
        // Serialize the entire $_POST data
        $serialized_post_data = serialize($_POST);

        // Save listing to custom post type with 'pending' status
        $post_data = array(
            'post_title'   => sanitize_text_field($listing_data['title']),
            'post_content' => sanitize_textarea_field($listing_data['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending',
        );
        $post_id = wp_insert_post($post_data);

        // Assign categories to the post (only existing categories)
        if ($post_id) {
            // Check if categories are set in the form
            if (!empty($_POST['categories'])) {
                $selected_categories = array_map('intval', $_POST['categories']); // Ensure category IDs are integers

                // Get existing category IDs from 'gd_placecategory'
                $existing_categories = get_terms(array(
                    'taxonomy'   => 'gd_placecategory',
                    'hide_empty' => false,
                    'fields'     => 'ids', // Get only IDs
                ));

                // Filter selected categories to include only existing ones
                $valid_categories = array_intersect($selected_categories, $existing_categories);

                // Assign only existing categories to the post
                if (!empty($valid_categories)) {
                    wp_set_object_terms($post_id, $valid_categories, 'gd_placecategory');
                }
            }

            // Store serialized POST data as post meta
            update_post_meta($post_id, 'serialized_post_data', $serialized_post_data);

            // Redirect to the same page with a success parameter
            $current_url = home_url(add_query_arg(array(), $_SERVER['REQUEST_URI']));
            $redirect_url = add_query_arg('submission', 'success', $current_url);
            wp_redirect($redirect_url);
            exit; // Always call exit after wp_redirect to prevent further code execution
        }
    }
}

### Explanation:
1. **Fetch Existing Categories**:
– We use `get_terms()` with `'fields' => 'ids'` to fetch only the IDs of existing categories in the `gd_placecategory` taxonomy.

2. **Filter Selected Categories**:
– We use `array_intersect()` to ensure that only the selected categories that already exist are assigned to the post.

3. **Assign Categories**:
– If any valid categories are found, they are assigned using `wp_set_object_terms()`.

### Form HTML (No Change Required):

Ensure your form is passing valid category IDs:

<form method="POST">
    <label for="listing_title">Title:</label>
    <input type="text" name="listing_data[title]" required><br>

    <label for="listing_content">Content:</label>
    <textarea name="listing_data[content]" required></textarea><br>

    <label for="categories">Select Categories:</label>
    <select name="categories[]" multiple>
        <?php
        // Fetch categories from 'gd_placecategory'
        $categories = get_terms(array(
            'taxonomy'   => 'gd_placecategory',
            'hide_empty' => false,
        ));
        foreach ($categories as $category) {
            echo '<option value="' . esc_attr($category->term_id) . '">' . esc_html($category->name) . '</option>';
        }
        ?>
    </select><br>

    <input type="submit" name="custom_listing_form" value="Submit Listing">
</form>

### Key Improvements:
– **Prevent Creation of New Categories**: Ensures only existing categories are assigned, preventing WordPress from creating new ones.
– **More Robust Validation**: Checks that only valid IDs are passed to `wp_set_object_terms()`.

This updated code will assign categories correctly to your custom post type without creating new ones.

User
gd_placecategory has those term ids
Assistant
If the custom taxonomy `gd_placecategory` already has the specific term IDs you want to assign and WordPress is still creating new categories, it might be due to the way the term IDs are being passed or handled. To ensure that existing terms are assigned correctly, let's enhance the code by:

1. **Fetching existing term IDs** directly from the `gd_placecategory` taxonomy.
2. **Ensuring only valid term IDs** are used when assigning categories.

Here's the revised code:

### Updated Code to Assign Existing `gd_placecategory` Terms:

// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['custom_listing_form'])) {
    $listing_data = isset($_POST['listing_data']) ? $_POST['listing_data'] : array(); // Retain submitted data

    // Validate form data (simple validation example)
    $errors = array();
    if (empty($listing_data['title'])) {
        $errors[] = 'Title is required.';
    }
    if (empty($listing_data['content'])) {
        $errors[] = 'Content is required.';
    }

    if (empty($errors)) {
        // Serialize the entire $_POST data
        $serialized_post_data = serialize($_POST);

        // Save listing to custom post type with 'pending' status
        $post_data = array(
            'post_title'   => sanitize_text_field($listing_data['title']),
            'post_content' => sanitize_textarea_field($listing_data['content']),
            'post_type'    => 'dummy_listing',
            'post_status'  => 'pending',
        );
        $post_id = wp_insert_post($post_data);

        // Assign categories to the post (only existing categories)
        if ($post_id) {
            // Check if categories are set in the form
            if (!empty($_POST['categories'])) {
                $selected_categories = array_map('intval', $_POST['categories']); // Ensure category IDs are integers

                // Get all existing term IDs from 'gd_placecategory'
                $existing_categories = get_terms(array(
                    'taxonomy'   => 'gd_placecategory',
                    'hide_empty' => false,
                    'fields'     => 'ids', // Fetch only term IDs
                ));

                // Check if the selected category IDs exist in 'gd_placecategory'
                $valid_categories = array_intersect($selected_categories, $existing_categories);

                // Assign only valid categories to the post
                if (!empty($valid_categories)) {
                    wp_set_object_terms($post_id, $valid_categories, 'gd_placecategory', false);
                }
            }

            // Store serialized POST data as post meta
            update_post_meta($post_id, 'serialized_post_data', $serialized_post_data);

            // Redirect to the same page with a success parameter
            $current_url = home_url(add_query_arg(array(), $_SERVER['REQUEST_URI']));
            $redirect_url = add_query_arg('submission', 'success', $current_url);
            wp_redirect($redirect_url);
            exit; // Always call exit after wp_redirect to prevent further code execution
        }
    }
}

### Key Changes Explained:
1. **Fetching Existing Term IDs**:
– `get_terms()` is used with `'fields' => 'ids'` to get a list of all existing term IDs in the `gd_placecategory` taxonomy.

2. **Validating Selected Categories**:
– `array_intersect()` ensures that only terms that exist in `gd_placecategory` are assigned. This prevents WordPress from creating new categories.

3. **Assigning Terms**:
– The `wp_set_object_terms()` function is called with the validated category IDs.
– The fourth parameter (`false`) ensures that existing terms are replaced instead of being appended.

### HTML Form for Category Selection:

Make sure your form includes valid category IDs:

<form method="POST">
    <label for="listing_title">Title:</label>
    <input type="text" name="listing_data[title]" required><br>

    <label for="listing_content">Content:</label>
    <textarea name="listing_data[content]" required></textarea><br>

    <label for="categories">Select Categories:</label>
    <select name="categories[]" multiple>
        <?php
        // Fetch existing categories from 'gd_placecategory'
        $categories = get_terms(array(
            'taxonomy'   => 'gd_placecategory',
            'hide_empty' => false,
        ));
        foreach ($categories as $category) {
            echo '<option value="' . esc_attr($category->term_id) . '">' . esc_html($category->name) . '</option>';
        }
        ?>
    </select><br>

    <input type="submit" name="custom_listing_form" value="Submit Listing">
</form>

### Explanation of the Form:
– The form allows users to select multiple categories from the existing terms of `gd_placecategory`.
– The selected categories are passed as an array in `$_POST['categories']`.

### Final Notes:
– The use of `array_map('intval', $_POST['categories'])` ensures that the input values are treated as integers.
– The validation prevents creating new categories by only using existing term IDs.
– Ensure that the `gd_placecategory` taxonomy is correctly registered for your custom post type `dummy_listing`.

Leave a Comment

Your email address will not be published. Required fields are marked *