CMB2

Use CMB2 to Create a New Post Submission Form

Here at WebDevStudios, we try to use CMB2 for pretty much anything form or field related.

We do this for several reasons:

  1. It saves us time. It’s a framework that handles a lot of the nitty-gritty for us (creating markup, handling sanitizing, escaping and saving data, etc).
  2. It provides a lot of power. We can build awesome things quicker because CMB2 exists.
  3. We’re eating our own dogfood so that we can continue to hone the tool and make it as helpful as possible (see #1 & #2).

it's free software!

One of the cool and powerful ways we have used CMB2 is for creating front-end submission forms for our users. Traditionally we (and I’m sure many of you) have used Gravity Forms as your go-to front-end form solution. No doubt Gravity Forms is an incredible tool, but we decided for some use-cases, it isn’t the right tool.

For example, if I want to provide a front-end blog post (or other post-type) submission form, I want to avoid Gravity Forms. With a submission form like that, you generally want full control of how it looks, when/where it should display and where the data goes when submitted. You probably don’t want that form created in wp-admin where the site’s owner or users can edit it. Enter CMB2.

I’m going to walk you through how to create a front-end submission form with CMB2.

First step, we need to register our form. This is standard form and field registration:

/**
 * Register the form and fields for our front-end submission form
 */
function wds_frontend_form_register() {
    $cmb = new_cmb2_box( array(
        'id'           => 'front-end-post-form',
        'object_types' => array( 'post' ),
        'hookup'       => false,
        'save_fields'  => false,
    ) );

    $cmb->add_field( array(
        'name'    => __( 'New Post Title', 'wds-post-submit' ),
        'id'      => 'submitted_post_title',
        'type'    => 'text',
        'default' => __( 'New Post', 'wds-post-submit' ),
    ) );

    $cmb->add_field( array(
        'name'    => __( 'New Post Content', 'wds-post-submit' ),
        'id'      => 'submitted_post_content',
        'type'    => 'wysiwyg',
        'options' => array(
            'textarea_rows' => 12,
            'media_buttons' => false,
        ),
    ) );

    $cmb->add_field( array(
        'name'       => __( 'Featured Image for New Post', 'wds-post-submit' ),
        'id'         => 'submitted_post_thumbnail',
        'type'       => 'text',
        'attributes' => array(
            'type' => 'file', // Let's use a standard file upload field
        ),
    ) );

    $cmb->add_field( array(
        'name' => __( 'Your Name', 'wds-post-submit' ),
        'desc' => __( 'Please enter your name for author credit on the new post.', 'wds-post-submit' ),
        'id'   => 'submitted_author_name',
        'type' => 'text',
    ) );

    $cmb->add_field( array(
        'name' => __( 'Your Email', 'wds-post-submit' ),
        'desc' => __( 'Please enter your email so we can contact you if we use your post.', 'wds-post-submit' ),
        'id'   => 'submitted_author_email',
        'type' => 'text_email',
    ) );

}
add_action( 'cmb2_init', 'wds_frontend_form_register' );

As you can see, we’ve used the new CMB2 form and field registration API to register our front-end form and fields. We also used a new parameter from version 2.0.3: 'save_fields' => false. This prevents CMB2 from saving the fields, so we can do what we want with the submission.

We added fields for the post title, post content, and optional fields for the featured image, contributor name, and contributor email. The first item in the 'object_types' array will be used for the new post submission 'post_type'.

Now that we have our form registered, let’s go ahead and register a shortcode handler for our front-end submission form. This adds flexibility as the site owner can place this shortcode anywhere they want. We’ll also allow a few shortcode attributes that we’ll cover later.

/**
 * Handle the cmb-frontend-form shortcode
 *
 * @param  array  $atts Array of shortcode attributes
 * @return string       Form html
 */
function wds_do_frontend_form_submission_shortcode( $atts = array() ) {

    // Current user
    $user_id = get_current_user_id();

    // Use ID of metabox in wds_frontend_form_register
    $metabox_id = 'front-end-post-form';

    // since post ID will not exist yet, just need to pass it something
    $object_id  = 'fake-oject-id';

    // Get CMB2 metabox object
    $cmb = cmb2_get_metabox( $metabox_id, $object_id );

    // Get $cmb object_types
    $post_types = $cmb->prop( 'object_types' );

    // Parse attributes. These shortcode attributes can be optionally overridden.
    $atts = shortcode_atts( array(
        'post_author' => $user_id ? $user_id : 1, // Current user, or admin
        'post_status' => 'pending',
        'post_type'   => reset( $post_types ), // Only use first object_type in array
    ), $atts, 'cmb-frontend-form' );

    // Initiate our output variable
    $output = '';

    // Our CMB2 form stuff goes here

    return $output;
}
add_shortcode( 'cmb-frontend-form', 'wds_do_frontend_form_submission_shortcode' );

So we’ve registered our shortcode, cmb-frontend-form and supplied a callback, wds_do_frontend_form_submission_shortcode. Because our shortcode can accept a few attributes, we’re passing in the $atts array. This will be the array of attributes already parsed out for us by WordPress (if the shortcode has any attributes).

Next, you follow the inline comments to see what is happening.

We are:

  1. Getting the currently logged-in user’s ID (This will be the default post-author user ID for the new post).
  2. Creating our $metabox_id variable. It is the same value as the 'id' parameter we used when registering our metabox/form.
  3. Creating our $object_id variable. This looks hokey, but it allows us to prevent CMB2 from auto-magically finding an object id (which could end up being the post ID of the page the form lives on.. not what we want!). This will ensure that the form remains empty.
  4. Retrieving our $cmb metabox instance by passing the metabox/form id we registered in our first snippet, 'front-end-post-form'.
  5. Retrieving the 'object_types' property from the $cmb object. This object type will be the default 'post_type' for the new post submission (unless overridden by the shortcode attributes).
  6. Parsing the shortcode attributes. We’ll explain in a bit.
  7. Creating an empty $output variable that we’ll be appending (concatenating) too.

To parse the shortcode attributes, we use the shortcode_atts function. This function, according to the Codex, “…Combines user shortcode attributes with known attributes and fills in defaults when needed. The result will contain every key from the known attributes, merged with values from shortcode attributes.”

This allows us to specify some default attributes for our shortcode. In our case, our default 'post_author' for the submitted post will be the currently logged-in user or 1, the default submitted post’s status will be 'pending' and the default submitted post’s 'post_type' will be 'post'. If I wanted, I can change all those attributes by modifying the shortcode in my content like so: [cmb-frontend-form post_author=2 post_status="draft" post_type="page"].

Next, let’s add our CMB2 form to the shortcode output. We’re going to ‘zoom in’ a bit with our snippet. At the end, we’ll put it all together.

/**
 * Handle the cmb-frontend-form shortcode
 *
 * @param  array  $atts Array of shortcode attributes
 * @return string       Form html
 */
function wds_do_frontend_form_submission_shortcode( $atts = array() ) {

    // ... Previous function code omitted for brevity

    // Initiate our output variable
    $output = '';

    // Get our form
    $output .= cmb2_get_metabox_form( $cmb, $object_id, array( 'save_button' => __( 'Submit Post', 'wds-post-submit' ) ) );

    return $output;
}
add_shortcode( 'cmb-frontend-form', 'wds_do_frontend_form_submission_shortcode' );

With that new line, we’re actually retrieving the CMB2 form markup. The parameters we pass to the cmb2_get_metabox_formfunction include the $cmb object we just retrieved, the fake post id we created, and an array of arguments we want to override in the cmb2_get_metabox_form function. In our case, we only want to override the text of the 'save_button' to more accurately reflect what the button will be doing.

So now we have a form on the page, which is pretty exciting. At this point, you may want to take some time to add some custom styles to your theme’s stylesheet to make the form look how you want. But there is a key flaw: the form will not do anything when we submit. Our 'save_fields' => false ensures that. So let’s go ahead and create our submission handler.

/**
 * Handles form submission on save
 *
 * @param  CMB2  $cmb       The CMB2 object
 * @param  array $post_data Array of post-data for new post
 * @return mixed            New post ID if successful
 */
function wds_handle_frontend_new_post_form_submission( $cmb, $post_data = array() ) {

    // If no form submission, bail
    if ( empty( $_POST ) ) {
        return false;
    }

    // check required $_POST variables and security nonce
    if (
        ! isset( $_POST['submit-cmb'], $_POST['object_id'], $_POST[ $cmb->nonce() ] )
        || ! wp_verify_nonce( $_POST[ $cmb->nonce() ], $cmb->nonce() )
    ) {
        return new WP_Error( 'security_fail', __( 'Security check failed.' ) );
    }

    if ( empty( $_POST['submitted_post_title'] ) ) {
        return new WP_Error( 'post_data_missing', __( 'New post requires a title.' ) );
    }

    // Do WordPress insert_post stuff

    return $new_submission_id;
}

Our post-submission handler function, wds_handle_frontend_new_post_form_submission‘ takes two arguments, a CMB2 object, and an optional array of post data for the inserted post.

The first step is to check if the form has even been submitted. If not, we bail out early. If so, then we verify that all the security pieces are in place as well as the required post data. We’re requiring the user to at least submit a title for their post. If all goes well, we’re now ready to create our new post.

Now, let’s leverage a new method, get_sanitized_values, to sanitize the array of fields data submitted, We’ll pass it the $_POSTvariable. Once the values have been properly sanitized, let’s set our new post’s title and content from those fields and insert it!

/**
 * Handles form submission on save
 *
 * @param  CMB2  $cmb       The CMB2 object
 * @param  array $post_data Array of post-data for new post
 * @return mixed            New post ID if successful
 */
function wds_handle_frontend_new_post_form_submission( $cmb, $post_data = array() ) {

    // ... Previous function code omitted for brevity

    // Fetch sanitized values
    $sanitized_values = $cmb->get_sanitized_values( $_POST );

    // Set our post data arguments
    $post_data['post_title']   = $sanitized_values['submitted_post_title'];
    unset( $sanitized_values['submitted_post_title'] );
    $post_data['post_content'] = $sanitized_values['submitted_post_content'];
    unset( $sanitized_values['submitted_post_content'] );

    // Create the new post
    $new_submission_id = wp_insert_post( $post_data, true );

    // If we hit a snag, update the user
    if ( is_wp_error( $new_submission_id ) ) {
        return $new_submission_id;
    }

    return $new_submission_id;
}

Ok, Let’s handle the featured image and custom post meta for the new post:

/**
 * Handles form submission on save
 *
 * @param  CMB2  $cmb       The CMB2 object
 * @param  array $post_data Array of post-data for new post
 * @return mixed            New post ID if successful
 */
function wds_handle_frontend_new_post_form_submission( $cmb, $post_data = array() ) {

    // ... Previous function code omitted for brevity

    // If we hit a snag, update the user
    if ( is_wp_error( $new_submission_id ) ) {
        return $new_submission_id;
    }

    /**
     * Other than post_type and post_status, we want
     * our uploaded attachment post to have the same post-data
     */
    unset( $post_data['post_type'] );
    unset( $post_data['post_status'] );

    // Try to upload the featured image
    $img_id = wds_frontend_form_photo_upload( $new_submission_id, $post_data );

    // If our photo upload was successful, set the featured image
    if ( $img_id && ! is_wp_error( $img_id ) ) {
        set_post_thumbnail( $new_submission_id, $img_id );
    }

    // Loop through remaining (sanitized) data, and save to post-meta
    foreach ( $sanitized_values as $key => $value ) {
        update_post_meta( $new_submission_id, $key, $value );
    }

    return $new_submission_id;
}

You can see we’re using a helper function, wds_frontend_form_photo_upload, (which is just a wrapper for media_handle_upload). It’s not directly related to CMB2, so I won’t go over it here, but I’ll include it in the final code snippet.

After we upload our image to the new post, if all went well, we’ll make that image the post’s featured image (set_post_thumbnail).

And finally, we’ll loop through the rest of the sanitized field values and save them as post meta.

Now that we have our custom save handler, we need to incorporate it back into our shortcode handler function, wds_do_frontend_form_submission_shortcode.

/**
 * Handle the cmb-frontend-form shortcode
 *
 * @param  array  $atts Array of shortcode attributes
 * @return string       Form html
 */
function wds_do_frontend_form_submission_shortcode( $atts = array() ) {

    // ... Previous function code omitted for brevity

    // Initiate our output variable
    $output = '';

    // Handle form saving (if form has been submitted)
    $new_id = wds_handle_frontend_new_post_form_submission( $cmb, $atts );

    if ( $new_id ) {

        if ( is_wp_error( $new_id ) ) {

            // If there was an error with the submission, add it to our ouput.
            $output .= '<h3>' . sprintf( __( 'There was an error in the submission: %s', 'wds-post-submit' ), '<strong>'. $new_id->get_error_message() .'</strong>' ) . '</h3>';

        } else {

            // Get submitter's name
            $name = isset( $_POST['submitted_author_name'] ) && $_POST['submitted_author_name']
                ? ' '. $_POST['submitted_author_name']
                : '';

            // Add notice of submission
            $output .= '<h3>' . sprintf( __( 'Thank you %s, your new post has been submitted and is pending review by a site administrator.', 'wds-post-submit' ), esc_html( $name ) ) . '</h3>';
        }

    }

    // Get our form
    $output .= cmb2_get_metabox_form( $cmb, $object_id, array( 'save_button' => __( 'Submit Post', 'wds-post-submit' ) ) );

    return $output;
}
add_shortcode( 'cmb-frontend-form', 'wds_do_frontend_form_submission_shortcode' );

You can see, right after we intiate our $output variable, and just before we use cmb2_get_metabox_form to retrieve our form markup, we’re using our new wds_handle_frontend_new_post_form_submission function to save any submitted post entries. If it saves the post, it will return the new post ID, but if it hit a snag, will return a WP_Error object.

So if we got a response back from our save handler, we’re going to output a message to the user. If the submission process hit a snag, the user will be alerted with the proper error message (and otherwise be given a ‘success’ message). These messages get appended to the $output variable before the form markup so that they will show up at the top of the form.

One issue with this method is that if user hits refresh after submitting post, a new post will continue to be submitted. You should probably either check for an existing post with the submitted data BEFORE doing wp_insert_post, or implement a redirect when the form is submitted, but I’ll have to cover that another time. (Update 5/23/15: The snippet has been updated to now redirect on successful submission, woot!)

And there you have it! A flexible front-end submission form that you can use to generate new posts (or other post-types). You can use this for all kinds of ideas. Maybe you want a movie review site–just change the registered fields for your form to reflect the kind of data you would want for that review. Title, content, rating, submitter’s name/email, etc. You can see there is a lot of flexibility!

You can find the entire snippet at the CMB2 Snippet Library. If you haven’t yet checked out the CMB2 Snippet Library, you definitely should! There are a lot of tips and tricks (like this one!) throughout.

Comments

84 thoughts on “Use CMB2 to Create a New Post Submission Form

  1. So glad I found CMB2! Finally I can control all of my custom fields via Git. However, I think this article is missing a very important (to me at least) piece. What is the actual output when using this method of frontend forms? What does the HTML look like? How do you customize it?

      1. Thanks Justin! I think I’m just misunderstanding how to use CMB2 for a frontend form. For example, what if I want my form to be heavily customized? I have a rather large one that needs to be formatted with Bootstrap and divided into tabs. Is there a way to build my own HTML and then insert individual fields with a CMB2 function?

    1. CMB2 is very flexible and you can use it as granularly as possible, but you’ll likely have to be comfortable digging through the plugin and the different snippets available in order to do what you’re asking. If you really wanted, you could use CMB2 to register the fields, then create the field markup manually and use the get_sanitized_values to handle your sanitization before inserting to the database. There are many levels of abstraction you could use.

  2. Hi Justin,

    Do you think it´s also an good way to create forms, that user can edit their account? Example -> user avatar upload.

    Nice greetings from Vienna!

  3. Great tutorial.

    If i would like to redirect user to another page instead of showing some message how can I do this? I was trying wp_redirect but in the else loop where it output message but nothing happens.

    1. In order to do that, you would have to move the wds_handle_frontend_new_post_form_submission function hooked into the 'init' hook or something like that. It needs to happen before anything is output to the page (or a redirect will not work). The downside to that is that the $atts array from the shortcode parameters will not be available yet (unless you passed them through in the $_POST data). All that to say, it’s definitely doable, but outside the scope of this tutorial.

      1. Awesome Justin, thanks alot! Didn’t expect you to alter the code 🙂

        I have been also trying to add category for the post. I have tried to add ‘post_category’ to the wp_insert_post but since the form select form list category slugs as values instead of IDs it doesn’t work. Any suggestion on that?

  4. Great work and tutorial as well. It really shows the power of CMB2. I just wonder the following: In this front-end submit example you have the submission-handler which for example displays a message to the user on submit.

    If I have a simple cmb2 metabox brought to the front-end with cmb2_get_metabox_form I would like to also show messages to the user.

    Whats the best practice/hook to show different messages when the “save”-button is clicked like Your post is updated., please insert something in the xxx field and so on…

    or should i do this based on pre-save evaluation with javascript?

    best regards
    simon

  5. Great tutorial.

    I was following the new example that is on github but I ran into problem when I added second form. The second form doesn’t seem to save the data to WP. I had tried doing this earlier with the old code and with that it worked nicely but with new code it doesn’t seem to work. I would also like to use redirection so that why I started to use the newer code.

    I think it might have something to do that I have now twice the cmb2_after_init function.

  6. Hi, I created a custom post type and it works fine. I have another custom post type that I want to edit in the front end – displaying other fields than from my first custom post type. Can I use another metabox_id or another set of fields? How would you start to use setup another front end form?

    Thanks,
    Oliver

    function wds_frontend_cmb2_get() {
    // Use ID of metabox in wds_frontend_form_register
    $metabox_id = ‘front-end-post-form’;

  7. This is working flawlessly… great starting point.

    I have my form used in 2 places. What is the safest way to return early if the $_POST data is not from the form I want?

    Right now I have 2 conflicting `cmb2_after_init` functions to process the data.

    I’m messing with
    // Bail if not the form we need
    if ( $_POST['object_id'] != 'my-object-id' ) {
    return false;
    }

    …but I don’t really think that’s the best way. Any thoughts?

    1. As an update to myself… i’ve tried literally 100 things. I have 3 front end forms, 1 of which is used for 2 different things. The only consistent way I found to bail early is checking if object_id is the form object ID I want. I’m going with this until I find out otherwise.

  8. I have one quick question. When people did not put title, I see error message below on top of the form.
    “New post requires a title.”
    But the problem is that the title and content disappear and I am wondering if there is a way to keep form value until it is submitted successfully.

    Thanks.

  9. Hi,

    I am trying to get the post immediately published but even if I am using

    $atts = shortcode_atts( array(
    'post_author' => $user_id ? $user_id : 1, // Current user, or admin
    'post_status' => 'published',
    'post_type' => reset( $post_types ), // Only use first object_type in array
    ), $atts, 'cmb-frontend-form' );

    and I get a positive message about the post being published, I cannot see it anywhere in the Dashboard…. Any help?

    Thanks

  10. Hi, thanks for the very useful tutorial.

    Maybe I didn’t understand all the instructions (I’m a true newbie), but… Is it possible to use a frontend form in order to create a custom page with different custom metafields? I cannot figure out how can be possible to map the form fields with the meta fields.

    In other terms: I’ve created a custom page with some fields (dates, some text fields…) and I want that users can “fill” them with a form.

    Maybe the affair is much simpler than I believe…

    Thank you in advance for the help,
    Mauro

    1. Resolved using the same id for the corresponding form and metabox fields.

      It isn’t very elegant but it works (until first bug appears).

  11. When creating the new_cmb2_box in the cmb2_init action, is it possible to know which post we’re in? I’d like to be able to change which fields are part of the metabox based on certain metadata set for $post

    In my cmb2_init handler, I’ve tried doing:

    global $post; // <--- empty

    but it's empty.

    1. To conditionally show a metabox, you would use the 'show_on_cb' metabox parameter, and the callback registered there would receive the $cmb object as it’s argument, and the $cmb object would have an object_id property ($cmb->object_id) for retrieving the current ID in context.

      You can see this in use in the included example-functions.php file. Registered here and callback here. (in case the line numbers change in the links, the callback is 'yourprefix_show_if_front_page')

      This same concept applies to the 'show_on_cb' field parameter. The callback registered there would receive the $field object as it’s argument, and the $field object would have an object_id property ($field->object_id) for retrieving the current ID in context.

      You can see this in use in the included example-functions.php file. Registered here and callback here. (in case the line numbers change in the links, the callback is 'yourprefix_hide_if_no_cats')

  12. Hi!

    Thanks for the tutorial. I included the code as it is but i am facing a problem with the fact that the add media button does not work and i can not see the tinymce menu bar… what could be causing this?

  13. This is great – I love the CMB2 framework.

    I’d like to use this to make a custom registration form – how would I adapt this to work to register new users?

  14. Hi, nice tutorial and I love CMB2. I”ve used it in a number of big projects now and it has really boosted the power and flexibility of WordPress for me.

    I am interested in using the techniques in this tutorial to create a user registration type form. Do you think that is within the realms of its use? I’m not bothered about handling forgotten password stuff with it, just the initially user registration forms.

    1. It’s definitely possible! You could probably modify this tutorial to come up with something. I’ll see if any of our team has done something similar yet.

  15. This line ` $output .= cmb2_get_metabox_form( $cmb, $object_id, array( ‘save_button’ => __( ‘Submit Post’, ‘wds-post-submit’ ) ) );` does not change the submit button text. Thankfully, the plugin I’m creating is a one off for a client, so I changed it in the includes/helper-functions.php file 😀

  16. Thanks again for cmb2, you guys are the best! Do you know offhand what update may have caused the file field to stop working on when the frontend?

    I’m having the darndest time producing an error or seeing a ajax response. It just seems like the field (upload button) has gone silent. Thanks so much for any insight you might spare.

    1. Today I determined it works in WP 4.4.2 and then upon an update into 4.5.1 it stops cooperating. Unfortunately there’s no error to be found. I think the impact may be far reaching across the custom dev community if it is admin-ajax. Anyway, thanks ahead of time for any intel you come into. I’ll be looking at the changelog asap.

  17. I am sorry for this newbie question, but where do I put all those codes? My theme function.php or I must create new php file?

  18. hi
    I am facing issue that all my fields are saved from front end but except taxonomy_select.Here below is my code.Category only not saving others are saved.Kindly help me to solve.

    $cmb->add_field( array(
    ‘name’ => __( ‘Listing Category’, ‘wds-post-submit’ ),
    ‘desc’ => ‘Selct your listing category’,
    ‘id’ => ‘bee_listing_category’,
    ‘taxonomy’ => ‘listing_categories’, //Enter Taxonomy Slug
    ‘type’ => ‘taxonomy_select’,
    ) );

  19. hi
    sorry I do not know the English language
    I hope I can my intent
    I want to change the default code with the code

    add_action( ‘cmb2_init’, ‘fabreview_register_demo_metabox’ );
    function fabreview_register_demo_metabox() {
    $prefix = ‘_fabreview_meta_’;
    $cmb_review = new_cmb2_box( array(
    ‘id’ => $prefix . ‘metabox’,
    ‘object_types’ => array( ‘post’,)
    ));
    $group_field_id = $cmb_review->add_field( array(
    ‘id’ => ‘review_criteria’,
    ‘type’ => ‘group’,
    ‘options’ => array(
    ‘group_title’ => __(‘ hotel No. {#}’, ‘cmb’),
    ‘add_button’ => __(‘insert hotel’, ‘cmb’),
    ‘remove_button’ => __(‘remove’, ‘cmb’),
    ‘sortable’ => true,
    ),
    ));
    $cmb_review->add_group_field( $group_field_id, array(
    ‘name’ => ‘hotel name’,
    ‘id’ => ‘ts_hotel’,
    ‘type’ => ‘text’,
    ));
    $cmb_review->add_group_field( $group_field_id, array(
    ‘name’ => ‘price’,
    ‘id’ => ‘ts_price’,
    ‘type’ => ‘text’,
    ));
    }

    I’d use Mta box Group
    Please any help

  20. This snipet give me error like this:
    Fatal error: Call to undefined function cmb2_get_metabox() in xxx/public_html/CMB2/wp-content/themes/underscores/example-functions.php on line 63

    1. And now work perfect 😀

      if ( file_exists( dirname( __FILE__ ) . ‘/cmb2/init.php’ ) ) {
      require_once dirname( __FILE__ ) . ‘/cmb2/init.php’;
      } elseif ( file_exists( dirname( __FILE__ ) . ‘/CMB2/init.php’ ) ) {
      require_once dirname( __FILE__ ) . ‘/CMB2/init.php’;
      }

  21. Anyone have an idea on how to loop and show multiple forms for posts? I can loop through but all forms are showing same field entries… Looks like cmb2_get_metabox_form doesn’t like to work with multiple forms. Any ideas appreciated. Great post.

  22. Howdy. This looks like an awesome toolkit and I’ve seen it used on other plugins effectively. But, I seem to be missing a key step right out of the gate.

    How do I get the fields to show on the editor page for a custom post type? Is it one of the arguments when registering the post type?

    Creating the fields themselves looks pretty straight-forward. I just can’t seem to connect the dots so they show on the editor page.

    Thanks in advance for your help.

  23. Hi! Have a question how use “file_list” with frontend submission? If I just change file to file_list in this example files uploaded via medialibrary. How upload multiple files directly from PC like with singe file in example?

    1. Hi Bülent,

      A couple notes.

      First, this line `$cmb->save_fields( $new_submission_id, ‘post’, $sanitized_values );` is going to be passing in an empty array because the code before it keeps unsetting values. You’d want to pass in `$post_data` instead which houses the sanitized versions.

      Also, not quite sure which parts you want to save to post meta, but if push comes to shove, you can do so manually with the ID stored in `$new_submission_id`. Just use that with `update_post_meta` and the values you want stored.

      It’s very possible that the code snippets provided with the post don’t account for post meta because it’s hard to say exactly what meta a given user may want to save. That said, perhaps an example would have helped out at the same time.

  24. Hi all,

    First of all, I really appreciate all your talents and supports for me to be able to build a small website. Now I have got one simple question.

    How can I change value to be all lowercase? I am trying to change email value to be all lowercase so that all emails to be stored in lowercase regardless of the user’s input. Can you help me out what to add in the code below?

    $cmb->add_field( array(
    ‘name’ => __( ”, ‘wds-post-submit’ ),
    ‘default’ => $submitted_author_email,
    ‘id’ => ‘submitted_author_email’,
    ‘type’ => ‘text_email’,
    ‘attributes’ => array(
    ‘required’ => ‘required’,
    ‘placeholder’ => ’email:’,
    ),
    ) );

    Or is it something i should change in other area of the code?

    Thank you so much.

    an architect

    1. Hi an architect,

      You could do something as simple as adding “‘style’ => ‘text-transform: lowercase’,” to your list of attributes. It’ll set an inline style attribute that sets everything to lowercase.

      Full example:

      $cmb2->add_field( array(
      ‘name’ => ‘thing’,
      ‘default’ => ”,
      ‘id’ => ‘submitted_author_email’,
      ‘type’ => ‘text_email’,
      ‘attributes’ => array(
      ‘required’ => ‘required’,
      ‘placeholder’ => ’email:’,
      ‘style’ => ‘text-transform: lowercase’,
      ),
      ) );

  25. CMB2 at side of Kirki Toolkit have been the essential tools for me to develop great WordPress projects, this Front-end Submission will come in handy, but I’m having just one problem, at this point:

    $cmb->get_field( ‘submitted_post_title’ )->default() == $_POST[‘submitted_post_title’]

    This conditional is always returning true. For a hotfix I just pulled off, but I think there’s a reason for that code.

    Thank you!

    1. Hey Allyson,

      Can you provide an example of values returned for both sides of that comparison. Meaning the default value the left side is returning and a sample submitted title that are equating to true when they shouldn’t?

      Also a little confused at the moment with the `default()` method, as I’m not seeing that in CMB2’s core codebase at all.

  26. Hi,

    Thanks for this amazing tutorial. Everything in it works perfectly and I was able to make a very complex form very easily. However, I am facing an issue at the moment and I searched everywhere for a solution, but couldn’t find it. I made some metaboxes using CMB2 in the backend, in a custom post type. I also made a frontend form that will take all the values and create a new post in that post type with all the required information. I successfully made everything and all the data is saving in the database. The only thing that is not working is I am not getting that saved data in the metaboxes in the admin area. When I click on the Edit in the admin area of that post, all the metaboxes are empty. Can you suggest me some solution?

    1. Hey Kash,

      Some things, when you say “saved data in the metaboxes”, just to be certain, do you mean the ones being created by CMB2? or do you perhaps mean the default “Custom fields” metabox that comes with WordPress? If you do mean “Custom Fields” metabox, then the data will only show there if the meta keys aren’t prefixed with an underscore.

      Have you verified that the data is saved to the wp_postmeta table? Or is it missing from there as well? If it’s missing from there as well, then it sounds like a saving issue. If it’s present, then something else is going on.

      Can you share all of the relevant code for your CMB2 configuration? Perhaps via a GitHub Gist so that we can just paste a link instead of a bunch of code.

  27. Hi,
    I’m really a WordPress newbie and hate to ask such basic questions but at this stage I just need to. I’ve lifted this entire code from the snippets library and adapted (basically changed the name of the prefix) and placed in a plugin which was activated. Then I placed the shortcode on a page which when published and visited correctly displays the form. I key in all the fields (verbatim from your code, no changes) and then hit submit: no errors given but nothing gets written to the database. Screen is just refreshed and still contains all my entries.

    Is there some config step I’m missing?

    I’m on WP 4.9.5 and twentyseventeen theme.

      1. Yes I actually copied and pasted from the URL you quoted (referred to as ‘snippets library’ in my post) and was super-glad the form showed up on first attempt (I must say, not always the experience from free code lifted off the web.) I would appreciate and troubleshooting suggestions but will redo from scratch using a fresh dummy WP install till I hear from you.

  28. Dear Michael,

    Here’s what I did.
    – installed a fresh 4.9.5 WP using a newly created empty database
    – installed the plugin CMB2 and activated it
    – created a plugin file in the plugins folder
    – right after the plugin comments section, I have the if (file_exists…require_once code to include init.php
    – copied and pasted your code for the rest of the file (https://github.com/CMB2/CMB2-Snippet-Library/blob/master/front-end/cmb2-front-end-submit.php)
    – activated this plugin (no errors anywhere so far)
    – changed ‘save_fields’ to true
    – created a page having [cmb_frontend_form id=”front-end-post-form”]

    When I visit this page, it simply displays [cmb_frontend_form id=”front-end-post-form”]. It doesn’t even display the form.

    Has Webdev Studios tested this code to see it works properly in the newest WordPress? Or are there some tweaks to make it work?

    When I create and activate a separate plugin (in addition to the plugin described above) that has the code below, then the form shows up using [cmb-form id=”front-end-post-form”]. It then saves to the database but doesn’t clear fields after submission nor redirect. Also it seems to save the data as a revision of some old post because it does not show up in the list of posts for it to be approved into published status (it has post ID of 7 in the postmeta table.) I can’t remember where I got this other snippet from.

    add_shortcode( ‘cmb-form’, ‘cmb2_do_frontend_form_shortcode’ );
    /**
    * Shortcode to display a CMB2 form for a post ID.
    * @param array $atts Shortcode attributes
    * @return string Form HTML markup
    */
    function cmb2_do_frontend_form_shortcode( $atts = array() ) {
    global $post;
    /**
    * Depending on your setup, check if the user has permissions to edit_posts
    */
    if ( ! current_user_can( ‘edit_posts’ ) ) {
    return __( ‘You do not have permissions to edit this post.’, ‘lang_domain’ );
    }
    /**
    * Make sure a WordPress post ID is set.
    * We’ll default to the current post/page
    */
    if ( ! isset( $atts[‘post_id’] ) ) {
    $atts[‘post_id’] = $post->ID;
    }
    // If no metabox id is set, yell about it
    if ( empty( $atts[‘id’] ) ) {
    return __( “Please add an ‘id’ attribute to specify the CMB2 form to display.”, ‘lang_domain’ );
    }
    $metabox_id = esc_attr( $atts[‘id’] );
    $object_id = absint( $atts[‘post_id’] );
    // Get our form
    $form = cmb2_get_metabox_form( $metabox_id, $object_id );

    return $form;
    }

    1. It could be your theme/framework. I had the same issue when using the Automattic framework. The minute I switched to a different framework (I’m using Understrap now), it worked fine.

  29. Just wanted to say that this was the only thing online that was actually useful in setting up front facing forms with CMB2. The “tutorial” on github is as useful as nipples on a man. I realize it is free software, but I feel some things were written by Dr. Steve Brule. Front facing forms being numero uno on that list.

    CMB2 is by far the best at what it does within the WordPress ecosystem. Nothing comes close. ACF is pretty good, but doesn’t scale well considering how they made poor decisions early on and have basically created a parallel system that vaguely plays nicely with WP. Some of the others (Piklist & Carbon come to mind) might arguably be better than CMB2, but once again, have their own ecosystems that slightly stray away from WP, limiting your innate opportunities for innovation (and neither has a front facing form option that is known and online, as far as I’ve seen). PODs and Toolset have the same issues in terms of their own ecosystems, and Toolset especially…sucks. Sorry, it does.

    But CMB2? I’ve been able to do some pretty great things already with it. But it’s hard to innately figured things like the magic formula for front facing forms on your own, and if this tutorial didn’t exist, I wouldn’t be able to use this great thing.

    So, my point is that CMB2 isn’t Scientology so let’s not withhold information and make it a good ol’ boys sort of thing. It never will be something that grandma is going to use no matter what, so it will only get so big. But for those of us with the knowhow, make it just a wee bit easier to get over that hump.

  30. Hello,

    I love CMB2 and I am interested in using the techniques in this tutorial to create a user registration type form.

    have you done something similar , I would be very interested in the code>

    Thanks

    1. I imagine it’d be very much like what’s been posted originally, except during processing of the submission, you’d replace wp_insert_post() with wp_insert_user() and *_post_meta() calls with *_user_meta() calls. You’d also need to keep password creation and hashing in mind, using appropriate functions there. Worth tinkering around with

  31. Ive used CMB2 to create a pretty comprehensive, yet simple-to-use application for an end user or a user wanting to have a community.

    Once you get the hang of the Frontend Forms, boy oh boy things can take off in terms of bringing innovative ideas to life.

    Now that I’ve been able to get a good feel for CMB2 and frontend form creation – even for complex, conditional-based forms loading for a specific page w/ specific assets/dependencies loading for that page/form as well, my attention has shifted to leveraging the REST API with CMB2 Forms.

    Are there any in-depth tutorials or examples demonstrating the powerful use of CMB2 + REST API beyond the github docs?

    I found a LinkedIn demo, which I’ll explore (will pay for it…), but curious if there are other CMB2 Forms + REST API examples or tutorials?

  32. Hi,

    Thanks for this useful piece of code. Got it all working great for my site. Only thing we miss in the code is keeping the values of the inputs when there is a error returned.

    Is there a solution to keep the values if there is a error?

    1. Admittedly, I’m not seeing any great way for that topic. The issue is that the fields/output is all sort of “magically” constructed for you via CMB2’s core code.

      That said, it’d be more interesting, perhaps just to me, seeing why the WP_Errors are being returned, and addressing those moments. Hopefully those are pretty rare.

  33. Hi, I am hoping someone can help me.

    I have installed the version of the code currently in the snippet library into the local version of my theme (via `include_once`) and have CMB2 installed. I also have WordPress 5.2 installed (it hasn’t updated to 5.2.1 yet). The shortcode mentioned in the file also displays the form.

    So far, so good.

    However, when I try to submit the form, I am given the error message: “There was an error in the submission: Please enter a new title.” despite the fact that I have manually entered the information, including post title. I’m pretty sure I have missed something, but I am not sure what it could be. Could someone please help me?

    1. Hey Nathan,

      Can you provide the contents of your copy of `wds_handle_frontend_new_post_form_submission` ?

      I’m curious what exactly you have in that spot which is where the error shown would be coming through at.

  34. I performed all the procedures, used the new snippet I added the code to appear the fields in the front end, but does not create the posts

  35. Thanks Justin for such informative guide.
    I wonder how to post the form data to another web service, wordpress custom action, or even php file to proceed the collected data and pass to another web service like newsletter action url?

    1. Hey Amer,

      In the `wds_handle_frontend_new_post_form_submission()` function outlined above, you could use something like `wp_remote_post()` with the collected data you have there already and send it to wherever you feel need.

      1. Thanks Micheal,
        I’m newbie regarding php and wordpress, I’ll search more about this function.
        I’m already using default wordpress actions “admin_post” and it works fine with curl but I still can’t find a workaround for cmb2.

    1. Hi gnana,

      Tentatively saying yes, but I don’t know offhand exactly how much work would be needed to achieve that.

      The biggest thing would be fetching and populating the fields ahead of time so that the content can be revised, and then submitted. Perhaps grabbing the ID from the URL, and then using that with “get_post()” and passing each appropriate part as the value attribute for the CMB2 config.

      After the submission, it’d be a case of utilizing “wp_update_post()” and passing in the submitted values from the form.

  36. I was able to use CMB2 to add Metabox to the WooCommerce Add Product screen.

    If we can use Metabox to Create a New Post Submission Form, can you show us How we can use Metabox to Create a Frontend Submission Form for WooCommerce Products?

    Regards.

  37. I am using this code. Thank you for your great work.
    Everything works fine on my front-end. But unfortunately, when I test to submit something, I did not receive any submitted.
    My email admin work fine, I can receive notification when test to comment in post.
    Please help me.

    Thank you.

Have a comment?

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

accessibilityadminaggregationanchorarrow-rightattach-iconbackupsblogbookmarksbuddypresscachingcalendarcaret-downcartunifiedcouponcrediblecredit-cardcustommigrationdesigndevecomfriendsgallerygoodgroupsgrowthhostingideasinternationalizationiphoneloyaltymailmaphealthmessagingArtboard 1migrationsmultiple-sourcesmultisitenewsnotificationsperformancephonepluginprofilesresearcharrowscalablescrapingsecuresecureseosharearrowarrowsourcestreamsupporttwitchunifiedupdatesvaultwebsitewordpress