0

I'm in the process of building a custom plugin, and I'm having trouble saving meta data for the custom post type. The boxes display correctly in the editor, but when I click Publish or Save Draft, the values I've entered are not retained.

I've pasted snippets of my code below. I've followed a few tutorials, and looked through several questions here, but it looks like I've followed everything correctly, and I can't figure out where I went wrong. I'm sure it's something obvious.

Any help would be greatly appreciated.

Custom Post Type

// Register the Jobs post type
function fg_registerJobsPostType() {
    // Floodgate Jobs Post Type
    $fg_postType = 'fg_job';

    $jobsLabels = array(
        'name'               => _x( 'Floodgate Jobs', 'post type general name', '' ),
        'singular_name'      => _x( 'Floodgate Job', 'post type singular name', '' ),
        'menu_name'          => _x( 'Floodgate Jobs', 'admin menu', '' ),
        'name_admin_bar'     => _x( 'Floodgate Job', 'add new on admin bar', '' ),
        'add_new'            => __( 'Add New', 'Floodgate Job', '' ),
        'add_new_item'       => __( 'Add New Job', '' ),
        'new_item'           => __( 'New Job', '' ),
        'edit_item'          => __( 'Edit Job', '' ),
        'view_item'          => __( 'View Job', '' ),
        'all_items'          => __( 'All Jobs', '' ),
        'search_items'       => __( 'Search Jobs', '' ),
        'parent_item_colon'  => __( 'Parent Jobs:', '' ),
        'not_found'          => __( 'No Jobs found.', '' ),
        'not_found_in_trash' => __( 'No Jobs found in Trash.', '' ),
    );

    $jobsArgs = array(
        'labels'             => $jobsLabels,
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => array( 'slug' => 'fg_job' ),
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => 5,
        'supports'           => array( 'editor', 'custom-fields' )        
    );

    register_post_type( 'fg_job', $jobsArgs );
}
add_action( 'init', 'fg_registerJobsPostType' );

And The Metaboxes

// Register the Jobs Metaboxes
function fg_registerJobsMetabox() {
    /* Add meta boxes on the 'add_meta_boxes' hook. */
    add_action( 'add_meta_boxes', 'fg_addJobMetaboxes' );

    /* Save post meta on the 'save_post' hook. */
    add_action( 'wp_insert_post_data', 'fg_saveJobsMeta', 10, 2);
}
add_action( 'load-post.php', 'fg_registerJobsMetabox' );
add_action( 'load-post-new.php', 'fg_registerJobsMetabox' );

// Build the display components for the Jobs Metaboxes
function fg_addJobMetaboxes() {
    add_meta_box(
        'fg_jobDesc',      // Unique ID
        esc_html__( 'Job Description', 'Business Cards' ),    // Title
        'fg_jobDescMetabox',   // Callback function
        'fg_job',         // Admin page (or post type)
        'side',         // Context
        'default'         // Priority
    );

    add_meta_box(
        'fg_jobStatus',      // Unique ID
        esc_html__( 'Job Status', 'Printing' ),    // Title
        'fg_jobStatusMetabox',   // Callback function
        'fg_job',         // Admin page (or post type)
        'side',         // Context
        'default'         // Priority
    );

    add_meta_box(
        'fg_jobDueDate',      // Unique ID
        esc_html__( 'Due Date', '' ),    // Title
        'fg_jobDueDateMetabox',   // Callback function
        'fg_job',         // Admin page (or post type)
        'side',         // Context
        'default'         // Priority
    );

    add_meta_box(
        'fg_jobPriority',      // Unique ID
        esc_html__( 'Job Priority', '' ),    // Title
        'fg_jobPriorityMetabox',   // Callback function
        'fg_job',         // Admin page (or post type)
        'side',         // Context
        'default'         // Priority
    );
}

And the save functions

function fg_verifyMetaNonce($metaKey) {
    return  (isset($_POST[$metaKey . '_nonce']) && wp_verify_nonce( $_POST[$metaKey . '_nonce'], basename(__FILE__)));
}

function fg_saveMetaData($postID, $metaKey, $newMetaValue) {
    /* Get the meta value of the custom field key. */
    $metaValue = get_post_meta( $postID, $metaKey, true );

    /* If a new meta value was added and there was no previous value, add it. */
    if ( $newMetaValue && '' == $metaValue )
        update_post_meta( $postID, $metaKey, $newMetaValue, true );

    /* If the new meta value does not match the old value, update it. */
    elseif ( $newMetaValue && $newMetaValue != $metaValue )
        update_post_meta( $postID, $metaKey, $newMetaValue );

    /* If there is no new meta value but an old value exists, delete it. */
    elseif ( '' == $newMetaValue && $metaValue )
        delete_post_meta( $postID, $metaKey, $metaValue );
}


// Save the Jobs Metadata
function fg_saveJobsMeta($postID, $post) {
    /* Get the post type object. */
    $postType = get_post_type_object( $post->postType );

/* Check if the current user has permission to edit the post. */
if ( !current_user_can( $postType->cap->edit_post, $postID ) )
    return $postID;

    // Save the Job Description
    $metaKey = 'fg_jobDesc';

        /* Verify the nonce before proceeding. */
        if ( verifyMetaNonce($metaKey) )
            return $postID;

        /* Get the posted data and sanitize it for use as an HTML class. */
        $jobDesc = ( isset( $_POST[$metaKey] ) ? sanitize_text_field( $_POST[$metaKey] ) : '' );

    fg_saveMetaData($postID, $metaKey, $jobDesc);

    // Save the Job Status
    $metaKey = 'fg_jobStatus';

        /* Verify the nonce before proceeding. */
        if ( verifyMetaNonce($metaKey) )
            return $postID;

        /* Get the posted data and sanitize it for use as an HTML class. */
        $jobStatus = ( isset( $_POST[$metaKey] ) ? sanitize_text_field( $_POST[$metaKey] ) : '' );

        fg_saveMetaData($postID, $metaKey, $jobStatus);

    // Save the Job Description
    $metaKey = 'fg_jobDueDate';

        /* Verify the nonce before proceeding. */
        if ( verifyMetaNonce($metaKey) )
            return $postID;

        /* Get the posted data and sanitize it for use as an HTML class. */
        $jobDueDate = ( isset( $_POST[$metaKey] ) ? sanitize_text_field( $_POST[$metaKey] ) : '' );

        fg_saveMetaData($postID, $metaKey, $jobDueDate);

    // Save the Job Description
    $metaKey = 'fg_jobPriority';

        /* Verify the nonce before proceeding. */
        if ( verifyMetaNonce($metaKey) )
            return $postID;

        /* Get the posted data and sanitize it for use as an HTML class. */
        $jobPriority = ( isset( $_POST[$metaKey] ) ? sanitize_text_field( $_POST[$metaKey] ) : '' );

        fg_saveMetaData($postID, $metaKey, $jobPriority);
}
  • I tested your code on my side. There is so much bugs in your code that I stopped fixing after the first six, and believe me, there are still about 20 left, no jokes. You should set debug to true in wp-config, fix errors according, and if your code then still does not work after all the bug fixes, repost and explain where your code fails. – Pieter Goosen May 25 '14 at 18:51
  • Have a read here to debug wordpress – Pieter Goosen May 25 '14 at 19:11
  • Yeah, thanks. Kudos for making the effort, and I don't blame you for giving up. I kept trying after posting the question, and realized how far off I was. Kind of embarrassing, actually. – Mike May 25 '14 at 20:01
  • 1
    Thanks for pointing out the debug options too. To this point, I've been working on things simple enough that a well placed var_dump() was sufficient to resolve issues. This is my first foray into something more robust. After this mess, I started to look for a better debugging option. You saved me a lot of time. Much appreciated. – Mike May 25 '14 at 20:03
  • I'm glad I could point you in the right direction. +1 – Pieter Goosen May 26 '14 at 4:21
3

As the comments on the question indicated, this was basically a pile of garbage (my words, not theirs). There are a number of typos and inverted boolean checks. It was kind of a nightmare.

I managed to get the code working, and I don't like unanswered questions, so I wanted to work through the bugs from the top down and point out the solution that I ended up with.


Problem Area One

The first issue exists in registering the metaboxes:

// Register the Jobs Metaboxes
function fg_registerJobsMetabox() {
    /* Add meta boxes on the 'add_meta_boxes' hook. */
    add_action( 'add_meta_boxes', 'fg_addJobMetaboxes' );

    /* Save post meta on the 'save_post' hook. */
    add_action( 'wp_insert_post_data', 'fg_saveJobsMeta', 10, 2); // <- THIS IS THE ERROR
}
add_action( 'load-post.php', 'fg_registerJobsMetabox' );
add_action( 'load-post-new.php', 'fg_registerJobsMetabox' );

In this case, the add_action() call is incorrect. When using the wp_insert_post_data hook, the function parameters of my fg_saveJobsMeta() function were coming back with incorrect values.

Switching this to add_action( 'save_post', 'fg_saveJobsMeta', 10, 2) resolved this error


Problem Area Two

The next problem exists in the fg_saveJobsMeta($postID, $post) function:

function fg_saveJobsMeta($postID, $post) {
    /* Get the post type object. */
    $postType = get_post_type_object( $post->postType ); // <- THIS IS THE ERROR

    /* Check if the current user has permission to edit the post. */
    if ( !current_user_can( $postType->cap->edit_post, $postID ) )
        return $postID;
    [...]
}

the value of $postType was always being set to null, because $post->postType is incorrect. This should change to $post->post_type as post_type is the field on the actual post object.


Problem Area Three

The next issue is a little further down in the fg_saveJobsMeta() function:

// Save the Job Description
$metaKey = 'fg_jobDesc';

    /* Verify the nonce before proceeding. */
    if ( verifyMetaNonce($metaKey) ) // <- THIS IS THE ERROR
        return $postID;

    /* Get the posted data and sanitize it for use as an HTML class. */
    $jobDesc = ( isset( $_POST[$metaKey] ) ? sanitize_text_field( $_POST[$metaKey] ) : '' );

fg_saveMetaData($postID, $metaKey, $jobDesc);

There are two problems here.

  • verifyMetaNonce($metaKey) is the incorrect function.

The correct function call is fg_verifyMetaNonce($metaKey). The function name is prefixed to reduce the chances of collision with other plugins and/or themes.

  • The boolean check is backwards.

If fg_verifyMetaNonce($metaKey) returns true (meaning we have a valid nonce value), the function will return the $postID and exit, but it should save.

The correct call in this case would be if (!fg_verifyMetaNonce($metaKey). This will only exit the save process if the nonce value fails validation.

These errors were repeated several times (once for each of the custom fields I identified).


Problem Area Four

In solving the issue, I discovered that I had additional problems in code that I did not post on the original question.

There were a few errors in setting up the actual metabox UI elements (once again, repeated for each custom field). I copied one of the fields below:

// Generate the Job Description Metaboxes
function fg_jobDescMetabox() { // <- ERROR HERE
    wp_nonce_field( 'updating_job_meta', 'fg_jobDesc_nonce'); ?> // <- ERROR HERE

    <p>
        // ERROR HERE
        <input class="widefat" type="text" name="fg_jobDesc" id="fg_jobDesc"
        value="<?php echo esc_attr( get_post_meta( $post->ID, 'fg_jobDesc', true ) ); ?>" />
    </p>
<?php }

The correct code is as follows:

// Generate the Job Description Metaboxes
function fg_jobDescMetabox($object, $box) {
// ^ This function actually receives two arguments.
    wp_nonce_field( 'updating_job_meta', 'fg_jobDesc_nonce', true); ?>
    // ^ The wp_nonce_field() call needed a third boolean argument
    //   Without this argument, the nonce value was never set on the request.

    <p>
        <input class="widefat" type="text" name="fg_jobDesc" id="fg_jobDesc"
        value="<?php echo esc_attr( get_post_meta( $object->ID, 'fg_jobDesc', true ) ); ?>" />
        // ^ the global `$post` variable is null here, so using it did me no good.
        //   I need to use the $object parameter passed into the function instead. 
    </p>
<?php }

As mentioned above, this issue was repeated for each of my custom fields, so I needed to fix it in several places.


I've looked through the horribly bugged code and have compared it to my working version several times now. I believe that this covers all of the issues and differences between the two files.

  • Thanks for posting your answer. I must congratulate you on the excellent structure of your answer. +1. It is answers like this that keeps this site constructive – Pieter Goosen May 26 '14 at 4:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.