Ask your WordPress questions! Pay money and get answers fast! (more info)

Move an uploaded file on cpt post save WordPress

I'm using [[LINK href="https://github.com/WebDevStudios/CMB2"]]CMB2[[/LINK]] to upload files and need to move said uploaded files into a custom, specific subdirectory of the upload folder. I've tried filtering the up_upload_dir and that doesn't work, so I'm currently working with the concept of hooking into save_post to: 1) move the file to the directory, 2) update the attachment guid via update_attached_file, and 3) update the post meta.

I can do the first two just fine, but I can't get the post meta to correctly update. Sometimes it will update if I save again, but not always - and even so, it should be correct after I save the first time. Here is the code:


add_action( 'save_post', 'on_save', 10, 3 );
add_filter( 'cmb2_meta_boxes', 'metaboxes' );

public function metaboxes( $meta_boxes ) {
$prefix = "document_";

$meta_boxes["document_details"] = array(
'id' => "document_details",
'title' => "Document Details",
'object_types' => array( 'document' ), // post type
'context' => 'normal',
'priority' => 'high',
'show_names' => true,
'fields' => array(
array(
'name' => 'Delivery Date',
'id' => $prefix . 'date',
'type' => 'text_date_timestamp',
),
array(
'id' => $prefix . 'general',
'type' => 'group',
'description' => __( 'General Document' ),
'options' => array(
'group_title' => __( 'Document {#}' ),
'add_button' => __( 'Add Another Document' ),
'remove_button' => __( 'Remove Document' ),
),
'fields' => array(
array(
'name' => 'Document',
'id' => 'document_file',
'type' => 'file',
'options' => array(
'url' => false,
'add_upload_file_text' => 'Upload PDF',
),
),
array(
'name' => 'Description',
'id' => 'description',
'type' => 'text_medium',
),
),
),
),
);

return $meta_boxes;
}

/**
* Update things on the save
* @since 1.0.0
*/
public function on_save( $post_id, $post, $update ) {

// If this isn't the correct post type, don't update it.
if( 'document' != $post->post_type ) {
return;
}

// Bail out if running an autosave, ajax, cron, or revision.
if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
if( defined( 'DOING_CRON' ) && DOING_CRON ) {
return;
}
if( wp_is_post_revision( $post_id ) ) {
return;
}

// Bail out if the user doesn't have the correct permissions
if( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}

if( isset( $_POST['document_general'] ) && !empty( $_POST['document_general'] ) ) {
$new_docs = $_POST['document_general'];
foreach( $new_docs as $key => $doc ) {
if( isset( $doc["document_general_{$key}_document_file_id"] ) ) {
$success = $this->move_file( $doc["document_general_{$key}_document_file_id"] );
$success = move_file( $id );
$newfile = wp_get_attachment_url( $doc["document_general_{$key}_document_file_id"] );
if( $newfile )
$new_docs[ $key ]['document_file'] = $newfile;
}
}
update_post_meta( $post_id, 'document_general', $new_docs );
}
}

/**
* Move the file.
*
* @since 1.0.0
*
* @param string $file
* @return void
*/
public function move_file( $id ) {
// Check if the root directory is the custom directory.
// if not, move the file and change the guid
$path = get_attached_file( $id );

if( isset( $path ) && !empty( $path ) ) {
$filename = basename( $path ); // Create a new name
$newpath = CUSTOM_UPLOAD_DIR . '/' . $filename; // Get the complete file path

if( !file_exists( $path ) || file_exists( $newpath ) )
return false;

// Now, if the upload was successful we save a post meta with the filename, if not, save nothing
$success = rename( $path, $newpath );

if( $success ) {
return update_attached_file( $id, $newpath );
}
}
return false;
}

Answers (3)

2015-04-16

timDesain Nanang answers:

Hi Sir,
You can try this code:

add_filter('wp_handle_upload_prefilter', 'wpq_handle_upload_prefilter');
add_filter('wp_handle_upload', 'wpq_handle_upload');

function wpq_handle_upload_prefilter( $file ){
add_filter('upload_dir', 'wpq_media_upload_dir_change', 9999);
return $file;
}

function wpq_handle_upload( $fileinfo ){
remove_filter('upload_dir', 'wpq_media_upload_dir_change', 9999);
return $fileinfo;
}

function wpq_media_upload_dir_change($path){
if( !empty( $path['error'] ) ) return $path;

$post_id = $_REQUEST['post_id'];
$post_type = get_post_type($post_id);

if( 'document' != $post_type ) return $path;

$customdir = CUSTOM_UPLOAD_DIR;

$path['path'] = str_replace($path['subdir'], '', $path['path']); //remove default subdir (year/month)
$path['url'] = str_replace($path['subdir'], '', $path['url']);
$path['subdir'] = $customdir;
$path['path'] .= $customdir;
$path['url'] .= $customdir;

return $path;
}


ref:
http://wordpress.stackexchange.com/questions/25894/how-can-i-organize-the-uploads-folder-by-slug-or-id-or-filetype-or-author/53895


Joshua Nelson comments:

The problem with that method is that there is no $_REQUEST['post_id'] available via the media uploader when the wp_upload_dir is called (because it's called via ajax and the $_REQUEST only has three parameters: name (the file basename), action (upload-attachment) and a nonce).


timDesain Nanang comments:

I have tried my snippet, and working properly with your cmb2.
all files uploaded to /wp-content/uploads/docs
and "document_general" metapost have the right value.

add_filter( 'cmb2_meta_boxes', 'metaboxes' );
function metaboxes( $meta_boxes ) {
$prefix = "document_";
$meta_boxes["document_details"] = array(
'id' => "document_details",
'title' => "Document Details",
'object_types' => array( 'document' ), // post type
'context' => 'normal',
'priority' => 'high',
'show_names' => true,
'fields' => array(
array(
'name' => 'Delivery Date',
'id' => $prefix . 'date',
'type' => 'text_date_timestamp',
),
array(
'id' => $prefix . 'general',
'type' => 'group',
'description' => __( 'General Document' ),
'options' => array(
'group_title' => __( 'Document {#}' ),
'add_button' => __( 'Add Another Document' ),
'remove_button' => __( 'Remove Document' ),
),
'fields' => array(
array(
'name' => 'Document',
'id' => 'document_file',
'type' => 'file',
'options' => array(
'url' => false,
'add_upload_file_text' => 'Upload PDF',
),
),
array(
'name' => 'Description',
'id' => 'description',
'type' => 'text_medium',
),
),
),
),
);
return $meta_boxes;
}

//------------------------------------------------------------
//=upload dir change
//------------------------------------------------------------
//http://wordpress.stackexchange.com/questions/25894/how-can-i-organize-the-uploads-folder-by-slug-or-id-or-filetype-or-author/53895
add_filter('wp_handle_upload_prefilter', 'wpq_handle_upload_prefilter');
add_filter('wp_handle_upload', 'wpq_handle_upload');

function wpq_handle_upload_prefilter( $file ){
add_filter('upload_dir', 'wpq_media_upload_dir_change', 9999);
return $file;
}

function wpq_handle_upload( $fileinfo ){
remove_filter('upload_dir', 'wpq_media_upload_dir_change', 9999);
return $fileinfo;
}

function wpq_media_upload_dir_change($path){
if( !empty( $path['error'] ) ) return $path;

$post_id = $_REQUEST['post_id'];
$post_type = get_post_type($post_id);

if( 'document' != $post_type ) return $path;

$customdir = '/docs';

$path['path'] = str_replace($path['subdir'], '', $path['path']); //remove default subdir (year/month)
$path['url'] = str_replace($path['subdir'], '', $path['url']);
$path['subdir'] = $customdir;
$path['path'] .= $customdir;
$path['url'] .= $customdir;

return $path;
}


since the cmb2 using native wp uploader, then my snippet will working properly.


timDesain Nanang comments:

and here is the snippet for document_general (modified on the last part)

add_action( 'save_post', 'on_save', 10, 3 );
function on_save( $post_id, $post, $update ) {

// If this isn't the correct post type, don't update it.
if( 'document' != $post->post_type ) {
return;
}
// Bail out if running an autosave, ajax, cron, or revision.
if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
if( defined( 'DOING_CRON' ) && DOING_CRON ) {
return;
}
if( wp_is_post_revision( $post_id ) ) {
return;
}
// Bail out if the user doesn't have the correct permissions
if( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}

if( isset( $_POST['document_general'] ) && !empty( $_POST['document_general'] ) ) {
$new_docs = $_POST['document_general'];
foreach( $new_docs as $key => $doc ) {
if( isset( $doc["document_general_{$key}_document_file_id"] ) ) {
$new_docs[ $key ]['document_file'] = $newfile;
}
}
update_post_meta( $post_id, 'document_general', $new_docs );
}
}