On a site with multiple content types and various types of files being uploaded, I wanted to keep these uploaded files in a more organised manner rather than simply dumping in uploads or a year and month based parent directories. WordPress filters to the resue.

The code below modifies the upload directory to include the post type and year for files of type .pdf (based on filename). /uploads/post-type/year

Easily extended to other file types or filename check could be removed and apply to all uploads.

// Upload location modification

function am_mawa_pre_upload($file) {
add_filter('upload_dir', 'am_custom_upload_dir');
return $file;
}

function am_mawa_post_upload($fileinfo) {
remove_filter('upload_dir', 'am_custom_upload_dir');
return $fileinfo;
}

function am_custom_upload_dir($path) {
if (!empty($path['error']) || substr(strrchr($_POST['name'], '.'), 1) != 'pdf') {
return $path;
} //error or not pdf, so bail unchanged.

global $post, $post_id;
$post_id = (!empty($post_id) ? $post_id : (!empty($_REQUEST['post_id']) ? $_REQUEST['post_id'] : ''));
if (empty($post) || (!empty($post) && is_numeric($post_id) && $post_id != $post->ID)) {
$post = get_post($post_id);
}

$time = (!empty($_SERVER['REQUEST_TIME'])) ? $_SERVER['REQUEST_TIME'] : (time() + (get_option('gmt_offset') * 3600)); // Fallback of now
$post_type = 'post';
if (!empty($post)) {
// Grab the posted date for use later
$time = ($post->post_date == '0000-00-00 00:00:00') ? $time : strtotime($post->post_date);
$post_type = $post->post_type;
}

$date = explode(" ", date('Y m d H i s', $time));

$path = array(
'path' => WP_CONTENT_DIR . '/uploads/' . $post_type . '/' . $date[0], // Year on end
'url' => WP_CONTENT_URL . '/uploads/' . $post_type . '/' . $date[0],
'subdir' => '',
'basedir' => WP_CONTENT_DIR . '/uploads',
'baseurl' => WP_CONTENT_URL . '/uploads',
'error' => false,
);

return $path;
}