0

I'm trying to write a class to make meta boxes. The boxes display fine, but I can't seem to get the values to save. It seems like the Save function isn't being called at all.

As a further experiment, I added a meta value to the database manually, and it displayed just fine in the editor, but when I clicked "Update", the entry was deleted.

Any advice would be appreciated. Here's my code.

    /**
 * Reusable custom meta boxes class
 *
 * Reusable custom meta boxes
 * Part of Cloudsmith WP-Framework used to help create custom post types for Wordpress.
 *
 * @author  Cloudsmith Studio
 * @link    https://cloudsmith.ca
 * @version 1.0
 * @license 
 */

class Cloudsmith_Meta_Box {

    /**
     * Meta Box ID
     *
     * @var string $box_id Holds id of the custom meta box to be created.
     */
    public $box_id;

    /**
     * Meta Box Title
     *
     * @var string $box_title Holds the title of the meta box
     */
    public $box_title;

    /**
     * Meta Box Content
     *
     * @var string $box_context Holds the context parameter for the meta box
     */
    public $box_context;

    public $box_priority;

    public $box_post_type;

    /**
     * Meta Box Content
     *
     * @var array $box_meat_fields Holds an array of arrays for the fileds to be displayed in a meta box
     */
    public $box_meta_fields;


    public static function create( $title, $fields = array(), $post_type, $context = 'normal', $priority = 'high' ){

        // If the Parameters are Empty, Do nothing
        if( ! empty ( $title ) ){

            // Instanitate a new object for the meta box
            ${$title . '_meta_box'} = new Cloudsmith_Meta_Box;


            // Assign the properties
            ${$title . '_meta_box'}->box_id          = Cloudsmith_Utility::uglify( $title ) . '_meta_box';
            ${$title . '_meta_box'}->box_title       = Cloudsmith_Utility::prettify( $title );
            ${$title . '_meta_box'}->box_context     = $context;
            ${$title . '_meta_box'}->box_priority    = $priority;
            ${$title . '_meta_box'}->box_post_type   = $post_type;
            ${$title . '_meta_box'}->box_meta_fields = $fields;

            $meta_box = ${$title . '_meta_box'};
            add_action( 'admin_init', 
                function() use( $meta_box ) {

                    add_meta_box(
                        $meta_box->box_id ,
                        $meta_box->box_title,
                        [ $meta_box, 'display_meta_box' ],
                        $meta_box->box_post_type,
                        $meta_box->box_context,
                        $meta_box->box_priority,
                        $meta_box->box_meta_fields
                    );
                    add_action('save_post', [ $meta_box, 'save_meta_value' ] );
                }
            );

        }
     } // End of Create Method

     public function display_meta_box(){


        global $post;

        // use nonce for input verification
        echo '<input type="hidden" name="' . $this->box_id . '_meta_box_nonce" value="'.wp_create_nonce( basename( __FILE__ ) ).'" />';

        // loop through $fields array to create form inputs.
        echo '<table class="form-table">';
        foreach ( $this->box_meta_fields as $field ){

            // Get value for this field if it exists
            $meta_value = get_post_meta( $post->ID, $field['id'], true );

            // Begin table row
            echo
                '<tr>
                    <th><label for="' . $field['id'] . '">' . $field['label'] . '</label></th>
                    <td>';

                        // display HTML based on the type of input
                        switch( $field['type'] ){

                            // Text input
                            case 'text':
                                echo '<input type="text" name="' . $field['id'] . '" id="' . $field['id'] . '" value="'. $meta_value . '" size="30" />';
                                echo '</br><span class="description">' . $field['desc'] . '</span>';
                            break;

                            // Text Area Input
                            case 'textarea':
                                echo '<textarea name="' . $field['id'] . '" id="' . $field['id'] . '" cols="60" rows="4">'. $meta_value . '</textarea>';
                                echo '</br><span class="description">' . $field['desc'] . '</span>';
                            break;

                            // Select Box input
                            case 'select':
                                echo '<select>';
                                while ( list( $key, $value ) = each( $field['options'] ) ){
                                    echo '<option ', $meta_value == $value ? 'selected="selected"' : '', 'value="' . $value .'">' . $key . '</option>';
                                }
                                echo '</select><br>';
                                echo '<span class ="description">' . $field['desc'] . '</span>';
                            break;

                            // Checkbox input

                            case 'checkbox':
                                echo '<input type="checkbox" name="'. $field['id'] .'" id="' . $field['id'] .'"', $meta_value ? 'checked="checked"' : '','/>';
                                echo '<label for"' . $field['id'] . '">' . $field['desc'] . '</label>';
                            break;

                            // Single Select Radio Box input
                            case 'radio':

                            break;

                        } // End of switch
                    echo 
                    '</td>
                </tr>';
        } // End foreach

        echo '</table>'; // end of input table
     }

     public function save_meta_value( $post_id ) {

        global $post;

        echo '<div class="notice updated"><p>Saved!!!??</p></div>';
        //var_dump($this->box_meta_fields);

        // verify nonce
        if (!wp_verify_nonce($_POST[$this->box_id . '_meta_box_nonce'], basename(__FILE__))) 
            return $post_id;
        // check autosave
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
            return $post_id;
        // check permissions
        if ('page' == $_POST['post_type']) {
            if (!current_user_can('edit_page', $post_id))
                return $post_id;
            } elseif (!current_user_can('edit_post', $post_id)) {
                return $post_id;
        }

        // loop through fields and save the data
        foreach ($this->box_meta_fields as $field) {
            $old = get_post_meta($post_id, $field['id'], true);
            $new = $_POST[$field['id']];
            if ($new && $new != $old) {
                update_post_meta($post_id, $field['id'], $new);
            } elseif ('' == $new && $old) {
                delete_post_meta($post_id, $field['id'], $old);
            }
        } // end foreach
    }
} // End of Meta Box Class

And then I call my methods here:

    // Create a new custome post type for quizzes.
$quizzes = new Cloudsmith_Custom_Post_Type( 'Quiz', 'Quizzes', $args );

// Add taxonomies to the quiz post type
Cloudsmith_Taxonomy::create( 'quiz_category', 'quiz', 'Category', 'Categories', ['hierarchical' => true] );
Cloudsmith_Taxonomy::create( 'quiz_tags', 'quiz', 'Tag', 'Tags' );
Cloudsmith_Taxonomy::create( 'quiz_difficulty', 'quiz', 'Difficulty Level', 'Difficulty Levels', 
    // custom taxonomy settings
    [
        'hierarchical' => true,
        'show_in_menu' => false
    ] 
);

Cloudsmith_Meta_Box::create( 'Questions',
    [
        [
            'label' => 'First Question',
            'desc'  => 'What is the question you want to ask?',
            'id'    => "Question One",
            'type'  => 'text'
        ],
        [
            'label' => 'Second Question',
            'desc'  => 'Do you have another question?',
            'id'    => 'Question Two',
            'type'  => 'checkbox'
        ],
        [
            'label' => 'Third Question',
            'desc'  => 'How many more?',
            'id'    => 'Question Three',
            'type'  => 'textarea'
        ],
        [
            'label' => 'Fourth Question',
            'desc'  => 'At least 2',
            'id'    => 'Question 4',
            'type'  => 'select',
            'options' => 
                [
                    'option 1' => 'one',
                    'option 2' => 'two',
                    'option 3' => 'three'
                ]
        ]
    ],
    'quiz'
);

Cloudsmith_Meta_Box::create( 'Answers',
    [
        [
            'label' => 'First Answer',
            'desc'  => 'Do Two boxes work?',
            'id'    => "Answer One",
            'type'  => 'text'
        ]
    ],
    'quiz'
);
  • Isn't it a nonce value issue, look the source code to see the generated value, I think you'll never get the generated value with wp_create_nonce. – Benoti Jul 4 '17 at 5:49
  • I've tried running the code with all the security checks deleted, and still doesn't work. – Mandu Jul 4 '17 at 11:28
0

Figured it out:

There was a space in the field ID, and the $_POST key would collapse it. So the ID's weren't matching up in the foreach loop in my save function.

I solved this with a sanitizing function on the IDs for my meta fields.

I was further confused because my admin notices that I inserted to help me debug weren't showing. I didn't know that after saving the post, the page redirects, and the admin notices aren't seen by the user. Therefore, I thought the save_post action wasn't firing at all, when in fact it was.

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.