0

I've tried this on a fresh install on the twentyfifteen theme. I'm adding a metabox to pages which allows uploads - Whenever I upload a file on a new Page it uploads the file but also adds another file to the media library with the post_id as the name. It then saves the phantom ID as post meta. If I publish the post first then upload the file everything works as expected.

Here's my save_post hook, the code below is minimalistic with no validation just to replicate the issue.

/**
 * Save Metaboxes
 * @param int $post_id
 */
function save_custom_meta_boxes( $post_id ) {

    // If we're not in the right place, bailout
    if( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! isset( $post_id ) || ! isset( $_POST['post_type'] ) || ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    if( isset( $_FILES['_uploaded_file'] ) && ! empty( $_FILES['_uploaded_file']['name'] ) ) {
        require_once( ABSPATH . 'wp-admin/includes/image.php' );    
        $uploadStatus   = wp_handle_upload( $_FILES['_uploaded_file'], array( 'test_form' => false ) );
        $fileID         = wp_insert_attachment( array(
                'post_mime_type'    => $uploadStatus['type'],
                'post_title'        => preg_replace( '/\.[^.]+$/', '', basename( $uploadStatus['file'] ) ),
                'post_content'      => '',
                'post_status'       => 'inherit'
            ),
            $uploadStatus['file'],
            $post_id
        );
        $attachmentData = wp_generate_attachment_metadata( $fileID, $uploadStatus['file'] );
        wp_update_attachment_metadata( $fileID,  $attachmentData );
        update_post_meta( $post_id, '_uploaded_file', $fileID );
    }
}
add_action( 'save_post', 'save_custom_meta_boxes' );

Metabox - Here's the display of the metabox.

/** `page_meta` Callback Function **/
function page_meta_cb( $post ) { 
    wp_nonce_field( 'page_meta_metabox', 'page_meta_nonce' );
    $uploaded_file_id = get_post_meta( $post->ID, '_uploaded_file', true );
  ?>

    <table style="width:100%;">
        <tbody>
            <tr id="uploaded_file">
                <td style="width:8%;min-width:105px;">
                    <label for="uploaded_file_input" style="font-weight:bold;">Uploaded File</label><br />
                </td>
                <td>
                    <input type="file" style="width:100%;" name="_uploaded_file" value="" id="uploaded_file_input" />

                    <?php if( is_numeric( $uploaded_file_id ) ) : ?>

                        <div class="fileLink">
                            <a href="post.php?post=<?php echo $uploaded_file_id; ?>&action=edit" target="_blank">Edit File - <?php echo get_the_title( $uploaded_file_id ); ?></a>
                                <br />
                                <br />
                        </div>

                    <?php endif; ?>

                </td>
            </tr>
        </tbody>
    </table>

  <?php
} // END Metabox

The media library looks like:

Phantom Upload in Media Library

Again, the issue is only when uploading to new posts - published posts have no problem with the upload. What can the issue be?

3
  • How can you use an file input with the default form if it doesn't use multipart/form-data as the value of enctype attribute? With you actual code the file should not even be uploaded.
    – cybmeta
    Apr 29, 2015 at 16:41
  • @cybmeta I've added the enctype="multipart/form-data via post_edit_form_tag hook but I didn't think it was necessary to add to the question.
    – Howdy_McGee
    Apr 29, 2015 at 16:42
  • I think it is. I've been testing your code but I couldn't reproduce the issue because of that ;)
    – cybmeta
    Apr 29, 2015 at 16:43

2 Answers 2

1

I've modified your code a little to check that the post type is a page, that the current user can edit that page, verify the nonce and verify the inserting attachment functions. The resulting code is working. It seems that don't checking the post type could be the reason of the issue. Also, you don't need to manually include 'wp-admin/includes/image.php':

add_filter('post_edit_form_tag', function() {
    echo ' enctype="multipart/form-data"';
});

add_action( 'save_post', 'save_custom_meta_boxes', 10, 2 );
function save_custom_meta_boxes( $post_id, $post ) {

    // If we're not in the right place, bailout
    if( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! isset( $post_id ) || $post->post_type !== 'page' || ! current_user_can( 'edit_page', $post_id ) ) {
        return;
    }

    if ( !isset( $_POST['page_meta_nonce'] ) || ! wp_verify_nonce( $_POST['page_meta_nonce'], 'page_meta_metabox' ) ) {
        return;
    }

    if( ! empty( $_FILES['_uploaded_file']['name'] ) ) {  
        $uploadStatus   = wp_handle_upload( $_FILES['_uploaded_file'], array( 'test_form' => false ) );
        if ( $uploadStatus && ! isset( $uploadStatus['error'] ) ) {
            $fileID         = wp_insert_attachment( array(
                    'post_mime_type'    => $uploadStatus['type'],
                    'post_title'        => preg_replace( '/\.[^.]+$/', '', basename( $uploadStatus['file'] ) ),
                    'post_content'      => '',
                    'post_status'       => 'inherit'
                ),
                $uploadStatus['file'],
                $post_id
            );

            if( $fileID ) {
                $attachmentData = wp_generate_attachment_metadata( $fileID, $uploadStatus['file'] );
                wp_update_attachment_metadata( $fileID,  $attachmentData );
                update_post_meta( $post_id, '_uploaded_file', $fileID );
            }
        }
    }
}

add_action( 'add_meta_boxes_page', 'page_meta' );
function page_meta() {
    add_meta_box("page_upload_image", 'Upload image', 'page_meta_cb');
}
/** `page_meta` Callback Function **/
function page_meta_cb( $post ) { 
    wp_nonce_field( 'page_meta_metabox', 'page_meta_nonce' );
    $uploaded_file_id = get_post_meta( $post->ID, '_uploaded_file', true );
  ?>

  <div>

    <table style="width:100%;">
        <tbody>
            <tr id="uploaded_file">
                <td style="width:8%;min-width:105px;">
                    <label for="uploaded_file_input" style="font-weight:bold;">Uploaded File</label><br />
                </td>
                <td>
                    <input type="file" style="width:100%;" name="_uploaded_file" value="" id="uploaded_file_input" />

                    <?php if( is_numeric( $uploaded_file_id ) ) : ?>

                        <div class="fileLink">
                            <a href="post.php?post=<?php echo $uploaded_file_id; ?>&action=edit" target="_blank">Edit File - <?php echo get_the_title( $uploaded_file_id ); ?></a>
                                <br />
                                <br />
                        </div>

                    <?php endif; ?>

                </td>
            </tr>
        </tbody>
    </table>

    </div>

  <?php
} // END Metabox
3
  • 1
    Right, I think $post->post_type !== 'page' is really key. The $post_id could be of revision post type. So by relying on the $post_id and not checking if the type is a revision it created an additional media file based on the revision. I was using $_POST['post_type'] to check the post type which is also no reliable.
    – Howdy_McGee
    Apr 29, 2015 at 17:05
  • 1
    Oh, I forget hat 'revision' is a post type, not a post status. So both are in the same direction: post type seems to be the key. But I think you was wrong by checking the post type for 'revision', you actually needs to check that is a page, checking for 'revision' should be optional and complementary to cheking for page post type.
    – cybmeta
    Apr 29, 2015 at 17:12
  • 1
    Another note, to get around this issue I changed my initial conditional to something more simplified using the passed $post object - if( ! isset( $post ) || wp_is_post_autosave( $post ) || wp_is_post_revision( $post ) )
    – Howdy_McGee
    Apr 29, 2015 at 17:45
-1

Part of your problem might be that while you create a nonce you don't really use it.

You don't really need it to be a nonce, an hidden text input will be as good (yes, codex is wrong again ;) ). You use the hidden input as an indicator to when the save happened from the post edit screen, or technically when the meta box was displayed as part of the form. This should protect you when a user does a bulk edit and I assume that since revisions do not save meta values it should protect you for your specific problem.

1
  • Sorry for downvote, I know you don't like it. wp_nonce_field already outputs a hidden field. I can not imagine how replace it with another hidden field can be related with the issue of this question. You are right that nonces are not really needed, you can avoid them, but they can be very useful and recommended in a lot of situations. Anyway, the nonce field was generated but not checked by the OP, it couldn't be the cause of the issue.
    – cybmeta
    Apr 29, 2015 at 19:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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