I am coding my first Wordpress plugin that handles some data for a custom (shop-like) post type. It also has an image uploader within a metabox, where you simply drag in your images and it automatically handles the uploads, filename and media-library integration without reloading the page. (AJAX)

To do this I use Javascript for drag and drop and an AJAX submit to send the images to a wp-function in the backend. I use the media_handle_upload() that wordpress offers. It creates my thumbnails and does the upload process using wp_handle_upload() it also does the db-part for me which is pretty comfortable.

Code for this Part:

add_action('wp_ajax_save_attachments', function ($post) {
    $file = $_FILES['file'];

    if(!empty($file) && $file['size'] > 0) {

        if(isset($_POST['apt_post_id'])) {
            $post_id = $_POST['apt_post_id'];
            // retrieve the filetype & check if is allowed for upload
            $filetype = wp_check_filetype(basename($file['name']));
            $allowed_filetypes = array(
            if(in_array($filetype['type'], $allowed_filetypes)) {
                // Set up options array to add this file as an attachment
                $post_data = array(
                    "post_title"        => 'Testimage',
                    "post_content"      => "",
                    "post_status"       => "inherit",
                $overrides = array( 'test_form' => false );

                if( $attachment_id = media_handle_upload('file', $post_id, $post_data, $overrides) ) {
                    $attachments = get_attached_media('image', $post_id);
                    $attachment = get_object_vars($attachments[$attachment_id]);
                    $data = array(
                        'ID'        => $attachment['ID'],
                        'thumbnail' => wp_get_attachment_thumb_url($attachment['ID']),
                        'name'      => $attachment['post_title'],
                    echo json_encode(array(
                        'success'       => true,
                        'message'       => 'Image successfully uploaded!',
                        'attachment'    => $data,
            } else {
                // errorhandling
        } else {
                // errorhandling
    } else {
                // errorhandling

Now I want to upload the images to a specific folder like /wp-root/wp-content/uploads/apartments/ to do this I found out that one can set a filter: add_filter( 'upload_dir', array($this, 'wpUploadDir') );which I did and tested with the "normal" (non-ajax) upload and it worked fine.

But doing the same with an AJAX upload has no effect at all. It always loads the images to the ../2014/07/ folder. Is there any way to change this? I tried to put the filter-call in the 'admin_init' or 'init' or even before, but nothing seems to work.

I can't find anything on google that could solve my question, so I am here. Thank you for any hint and solution!

EDIT: The way how I integrated the filter call: At this state my class is pretty chaotic, but works fine so far... (the cleanup will follow). I try to only show necessary code because it would be more than 1000 lines of code if I'd post it all.

As I already mentioned, I tried 3 calls of the filter. Here you can see them all as comments.

new Zap('zap_apartments');

class Zap
    // some privates...

     *  Stores the upload folder for this post type
    private $_upload_dir = '/apartments';

    public function __construct($post_type_id)
        // set post_type_id by argument
        $this->post_type_id = $post_type_id;

        // set post_type as a new object from Fixture
        $this->post_type = new wpObjectsData($this->post_type_id);

        //add_filter( 'upload_dir', array($this, 'wpUploadDir') );

        // runs on plugin activation

        // run global wp init

        // run wp admin init

    public function wpInit($post)
        add_action('init', function($post) {

            // register post-type to wordpress

            // register taxonomies to wordpress

            //add_filter( 'upload_dir', array($this, 'wpUploadDir') );

    public function wpAdminInit()
        add_action('admin_init', function() {

            //add_filter( 'upload_dir', array($this, 'wpUploadDir') );

            // register wp scripts
            add_action( 'admin_enqueue_scripts', array($this, 'wpRegisterScripts') );

            // call the metaboxes

     *  Set new wp upload directory for custom post type
     *  @param  []  (required) array with _FILE structure
     *  @return []  array with new _FILE structure
    public function wpUploadDir($param)
        global $post;

        if($post->post_type == $this->post_type_id) {
            $param['path'] = $param['basedir'] . $this->_upload_dir;
            $param['url']  = $param['baseurl'] . $this->_upload_dir;

        return $param;

     *  Register all metaboxes for the plugin
    public function metaboxes()
        add_action('add_meta_boxes', function() {
                    //   $id (css),      $title,        $callback,              $post_type,         $context,   $priority,  $callback_args
            add_meta_box('zap_coredata', 'Stammdaten',  'apt_coredata',         'zap_apartments',   'normal',   'high');
            add_meta_box('zap_features', 'Merkmale',    'apartment_features',   'zap_apartments',   'normal',   'high');
            add_meta_box('zap_images',   'Bilder',      'apt_images',           'zap_apartments',   'normal',   'high');

        // tons of code here

        //// WP AJAX: UPLOAD FILES
        add_action('wp_ajax_save_attachments', function () {



        // tons of code here... yes i need to clean up soon, I know! ;-)

Here's a Picture of the callback right after the dropped image has uploaded

  • Could you please show how and where you added that filter callback? Also you should use wp_send_json_success() and wp_send_json_error() instead of echo json_encode(); die;. – kaiser Jul 29 '14 at 10:17
  • You might also want to use pathinfo() instead of the basename() in wp_check_filetype(basename($file['name'])); as it's much more focused and reliable. The basename() depends on the root which could defer between OS. – kaiser Jul 29 '14 at 10:19
  • @kaiser: thanks a lot for the 2 hints. I will integrate that in my final version. I have edited the post and added the required infos. I decided to use a class but did not consequently integrated it oop yet. – Raphael Jul 29 '14 at 11:22
  • Raphael, have you checked that your callback works? Add the following in your callback: exit( 'THIS WORKS!!1!' );. Then inspect your console (network tab) to see the preview of what happens inside the callback. – kaiser Jul 29 '14 at 12:29
  • What callback exactly do you mean? the ajax? yes that works... i've tested it all and I am already operating with it afterwards: showing the images right after the upload and edit a caption for it or even delete it. That only works because the response header sent me post_id of the attachment. – Raphael Jul 29 '14 at 12:55

Your Answer

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

Browse other questions tagged or ask your own question.