33

I know that plupload is going to be the new upload engine for WordPress 3.3 but I was wondering if there is any documentation yet on how it integrates with WordPress.

My specifically how to collect a response from the plUpload jQuery object once it has uploaded the media that you want and how one would use the same functionality in a meta box to create a gallery?

Has anyone played around with it, yet?

3
  • Thanks for the bounty, though there is a good chance an answer won't come until WordPress 3.3 has an official release Commented Nov 22, 2011 at 22:23
  • 3
    There's also a good chance I'll take a look at it this weekend :-) I've been using 3.3 for months now, and need to write up this exact thing before the first RC drops ...
    – EAMann
    Commented Nov 22, 2011 at 23:39
  • Here is a link to the jQuery plugin the new uploader uses, plupload (plupload.com). I have the gist of how they implement it but can't tell how the new implementation receives responses when a file is successfully uploaded. Commented Nov 22, 2011 at 23:51

3 Answers 3

18
+400

My specifically how to collect a response from the plUpload jQuery object once it has uploaded the media that you want and how one would use the same functionality in a meta box to create a gallery?

There's a specific file that handles this functionality: /wp-includes/js/plupload/handlers.dev.js. This file contains all of the hooks and triggers that tie Plupload (the third-party drag/drop multi-file system) to the uploader.

There are two events you might want to look at: "FileUploaded" and "Upload Complete"

FileUploaded

Remember, the new uploader is capable of uploading multiple files at once. So if there's something you want to do after each file in the queue is uploaded, you'll use jQuery to bind to this event.

WordPress, for example, binds the following:

uploader.bind('FileUploaded', function(up, file, response) {
    uploadSuccess(file, response.response);
});'

The uploadSuccess function here handles image thumbnails, fetches attachment meta from the server, and binds edit/delete buttons to the right object.

UploadComplete

The UploadComplete event will fire after everything in the queue is finished uploading. If you want to fire a general cleanup operation after the entire download is finished, this is what you'll want to bind to.

WordPress, for example, binds the following:

uploader.bind('UploadComplete', function(up, files) {
    uploadComplete();
});

The uploadComplete function here just enables the "Insert gallery" button on the page.

Unfortunately ...

... there doesn't seem to be a way for us to bind to these events. The uploader object exists within a closure in the handlers.js file, and Plupload itself doesn't have a way to reference existing instances. You can't use a simple jQuery selector to sniff it out and add a custom event ... so we're out of luck there.

On the one hand, you can use these custom events at-will in your own systems. Just spin up your own version of the handlers.js file with your own events and you can do whatever you want. But for the existing uploader, you're stuck with the existing API.

Keep in mind, that the new Pluploader calls the same methods at the same times as the old Flash uploader did. So my best guess is that any existing hacks or integrations you have should continue to work.

Testing that assumption

I have a plugin that uses the existing uploader to upload file attachments and display the URL in a custom meta field. It worked like magic with the old uploader, so I fired it up in WP 3.3 to see if it worked with the new uploader as well.

And it does!

So if you're already integrating with the media uploader, your system should still work with the new system without any changes.

22

(this is just a practical example based on EAMann's answer)

// include js
add_action('admin_enqueue_scripts', function($page){

  // check if this your page here with the upload form!
  if(($page !== 'post.php') || (get_post_type() !== 'post'))
    return;

  wp_enqueue_script('plupload-all');
});



// this adds a simple metabox with the upload form on the edit-post page
add_action('add_meta_boxes', function(){
  add_meta_box('gallery_photos', __('Photos'), 'upload_meta_box', 'post', 'normal', 'high');

});                                               



// so here's the actual uploader
// most of the code comes from media.php and handlers.js
function upload_meta_box(){ ?>
   <div id="plupload-upload-ui" class="hide-if-no-js">
     <div id="drag-drop-area">
       <div class="drag-drop-inside">
        <p class="drag-drop-info"><?php _e('Drop files here'); ?></p>
        <p><?php _ex('or', 'Uploader: Drop files here - or - Select Files'); ?></p>
        <p class="drag-drop-buttons"><input id="plupload-browse-button" type="button" value="<?php esc_attr_e('Select Files'); ?>" class="button" /></p>
      </div>
     </div>
  </div>

  <?php

  $plupload_init = array(
    'runtimes'            => 'html5,silverlight,flash,html4',
    'browse_button'       => 'plupload-browse-button',
    'container'           => 'plupload-upload-ui',
    'drop_element'        => 'drag-drop-area',
    'file_data_name'      => 'async-upload',            
    'multiple_queues'     => true,
    'max_file_size'       => wp_max_upload_size().'b',
    'url'                 => admin_url('admin-ajax.php'),
    'flash_swf_url'       => includes_url('js/plupload/plupload.flash.swf'),
    'silverlight_xap_url' => includes_url('js/plupload/plupload.silverlight.xap'),
    'filters'             => array(array('title' => __('Allowed Files'), 'extensions' => '*')),
    'multipart'           => true,
    'urlstream_upload'    => true,

    // additional post data to send to our ajax hook
    'multipart_params'    => array(
      '_ajax_nonce' => wp_create_nonce('photo-upload'),
      'action'      => 'photo_gallery_upload',            // the ajax action name
    ),
  );

  // we should probably not apply this filter, plugins may expect wp's media uploader...
  $plupload_init = apply_filters('plupload_init', $plupload_init); ?>

  <script type="text/javascript">

    jQuery(document).ready(function($){

      // create the uploader and pass the config from above
      var uploader = new plupload.Uploader(<?php echo json_encode($plupload_init); ?>);

      // checks if browser supports drag and drop upload, makes some css adjustments if necessary
      uploader.bind('Init', function(up){
        var uploaddiv = $('#plupload-upload-ui');

        if(up.features.dragdrop){
          uploaddiv.addClass('drag-drop');
            $('#drag-drop-area')
              .bind('dragover.wp-uploader', function(){ uploaddiv.addClass('drag-over'); })
              .bind('dragleave.wp-uploader, drop.wp-uploader', function(){ uploaddiv.removeClass('drag-over'); });

        }else{
          uploaddiv.removeClass('drag-drop');
          $('#drag-drop-area').unbind('.wp-uploader');
        }
      });

      uploader.init();

      // a file was added in the queue
      uploader.bind('FilesAdded', function(up, files){
        var hundredmb = 100 * 1024 * 1024, max = parseInt(up.settings.max_file_size, 10);

        plupload.each(files, function(file){
          if (max > hundredmb && file.size > hundredmb && up.runtime != 'html5'){
            // file size error?

          }else{

            // a file was added, you may want to update your DOM here...
            console.log(file);
          }
        });

        up.refresh();
        up.start();
      });

      // a file was uploaded 
      uploader.bind('FileUploaded', function(up, file, response) {

        // this is your ajax response, update the DOM with it or something...
        console.log(response);

      });

    });   

  </script>
  <?php
}


// handle uploaded file here
add_action('wp_ajax_photo_gallery_upload', function(){

  check_ajax_referer('photo-upload');

  // you can use WP's wp_handle_upload() function:
  $status = wp_handle_upload($_FILES['async-upload'], array('test_form'=>true, 'action' => 'photo_gallery_upload'));

  // and output the results or something...
  echo 'Uploaded to: '.$status['url'];

  exit;
});

There are more plupload events you can use, check out its documentation....

4
  • I've tried this code as is and so far it doesn't do anything. The image seems to upload but I don't know where and I get no response from the console Commented Dec 9, 2011 at 6:41
  • 1
    Okay, found the problem: for some reason the $_FILES['async-upload'] you sent to wp_handle_upload doesn't seem to pass a check in said function. If you pass array('test_form'=>false) as a second argument into wp_handle_upload, it uploads the file without a problem. There is also an extra parentheses in the call to add_meta_box. I added edits to your answer which should make it work. Commented Dec 9, 2011 at 7:21
  • 1
    As an implementation note - can set action to upload-attachment which will trigger native wp_ajax_upload_attachment() handler and with some tweaking don't need custom upload handler altogether, just the form and script parts.
    – Rarst
    Commented Feb 12, 2014 at 15:33
  • wp_localize_script() can pass server side data to the script on the front end Commented Jan 27, 2020 at 0:46
13

Here is an expansion to @One Trick Pony's answer. This, in addition to uploading the file to the proper, will also save said file as an attachment:

<?php
// include js
add_action('admin_enqueue_scripts', function($page){

  // check if this your page here with the upload form!
  if(($page !== 'post.php') || (get_post_type() !== 'post'))
    return;

  wp_enqueue_script('plupload-all');
});



// this adds a simple metabox with the upload form on the edit-post page
add_action('add_meta_boxes', function(){
  add_meta_box('gallery_photos', __('Photos'), 'upload_meta_box', 'post', 'normal', 'high');

});                                               



// so here's the actual uploader
// most of the code comes from media.php and handlers.js
function upload_meta_box(){ ?>
   <div id="plupload-upload-ui" class="hide-if-no-js">
     <div id="drag-drop-area">
       <div class="drag-drop-inside">
        <p class="drag-drop-info"><?php _e('Drop files here'); ?></p>
        <p><?php _ex('or', 'Uploader: Drop files here - or - Select Files'); ?></p>
        <p class="drag-drop-buttons"><input id="plupload-browse-button" type="button" value="<?php esc_attr_e('Select Files'); ?>" class="button" /></p>
      </div>
     </div>
  </div>

  <?php

  $plupload_init = array(
    'runtimes'            => 'html5,silverlight,flash,html4',
    'browse_button'       => 'plupload-browse-button',
    'container'           => 'plupload-upload-ui',
    'drop_element'        => 'drag-drop-area',
    'file_data_name'      => 'async-upload',            
    'multiple_queues'     => true,
    'max_file_size'       => wp_max_upload_size().'b',
    'url'                 => admin_url('admin-ajax.php'),
    'flash_swf_url'       => includes_url('js/plupload/plupload.flash.swf'),
    'silverlight_xap_url' => includes_url('js/plupload/plupload.silverlight.xap'),
    'filters'             => array(array('title' => __('Allowed Files'), 'extensions' => '*')),
    'multipart'           => true,
    'urlstream_upload'    => true,

    // additional post data to send to our ajax hook
    'multipart_params'    => array(
      '_ajax_nonce' => wp_create_nonce('photo-upload'),
      'action'      => 'photo_gallery_upload',            // the ajax action name
    ),
  );

  // we should probably not apply this filter, plugins may expect wp's media uploader...
  $plupload_init = apply_filters('plupload_init', $plupload_init); ?>

  <script type="text/javascript">

    jQuery(document).ready(function($){

      // create the uploader and pass the config from above
      var uploader = new plupload.Uploader(<?php echo json_encode($plupload_init); ?>);

      // checks if browser supports drag and drop upload, makes some css adjustments if necessary
      uploader.bind('Init', function(up){
        var uploaddiv = $('#plupload-upload-ui');

        if(up.features.dragdrop){
          uploaddiv.addClass('drag-drop');
            $('#drag-drop-area')
              .bind('dragover.wp-uploader', function(){ uploaddiv.addClass('drag-over'); })
              .bind('dragleave.wp-uploader, drop.wp-uploader', function(){ uploaddiv.removeClass('drag-over'); });

        }else{
          uploaddiv.removeClass('drag-drop');
          $('#drag-drop-area').unbind('.wp-uploader');
        }
      });

      uploader.init();

      // a file was added in the queue
      uploader.bind('FilesAdded', function(up, files){
        var hundredmb = 100 * 1024 * 1024, max = parseInt(up.settings.max_file_size, 10);

        plupload.each(files, function(file){
          if (max > hundredmb && file.size > hundredmb && up.runtime != 'html5'){
            // file size error?

          }else{

            // a file was added, you may want to update your DOM here...
            console.log(file);
          }
        });

        up.refresh();
        up.start();
      });

      // a file was uploaded 
      uploader.bind('FileUploaded', function(up, file, response) {

        // this is your ajax response, update the DOM with it or something...
        console.log(response);

      });

    });   

  </script>
  <?php
}


// handle uploaded file here
add_action('wp_ajax_photo_gallery_upload', function(){

  check_ajax_referer('photo-upload');

  // you can use WP's wp_handle_upload() function:
  $file = $_FILES['async-upload'];
  $status = wp_handle_upload($file, array('test_form'=>true, 'action' => 'photo_gallery_upload'));

  // and output the results or something...
  echo 'Uploaded to: '.$status['url'];

  //Adds file as attachment to WordPress
  echo "\n Attachment ID: " .wp_insert_attachment( array(
     'post_mime_type' => $status['type'],
     'post_title' => preg_replace('/\.[^.]+$/', '', basename($file['name'])),
     'post_content' => '',
     'post_status' => 'inherit'
  ), $status['file']);

  exit;
});
?>
1
  • 1
    Think there's a small mistake here--the last param of the wp_insert_attachment call should be $status['file'] rather than $status['url']. Pretty sure it needs to be the local path.
    – MathSmath
    Commented Dec 14, 2011 at 19:23

Your Answer

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

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