WordPress, files, AJAX

In my bitter ignorance I find the power of WordPress amazing. Files and AJAX are easy.

To me it’s a framework

While the idea of WordPress as a framework is disputed and controversial in a strict to the language significance WordPress is a framework. There are more times where I find myself thinking “I’d like to…” and find WordPress “has a function for it” than there are times where I find myself wishing WordPress would provide me with an easy way to do something.
Today began with the latter thought and ended up being a former one.

Files and AJAX “is a nightmare”

I’ve been requested a front-end registration form that would allow file uploading, all that done, possibly, using AJAX because it’s 2014 and reloading the page is so 2000. In my ignorance I’ve gone to Google and found some rather discouraging StackOverflow answers to the one question I had (‘how do I upload files via AJAX in WordPress?'); one in particular saying “files using AJAX is a nightmare”.
As usual a too specific question yielded partisan results.

jQuery form

A less specific question like “How do I do upload files via AJAX?” yielded the jQuery Form plugin and life seemed beautiful again.
First part done the other part, handling a file upload in WordPress, is easy using the wp_handle_upload function. And while I was going and try to decipher when and where each part of the jQuery plugin should have been loaded I found out WordPress packs it already in its scripts included and registered by default.

HTML, js, PHP

In a concise as possible code example given the HTML markup

<script>var ajax_url = "<?php echo admin_url('admin-ajax.php'); ?>"</script> 
<form id="file_form">
    <?php wp_nonce_field('ajax_file_nonce', 'security'); ?>
    <input type="hidden" name="action" value="my_file_upload">
    <label for="file_upload">It's a file upload...</label>
    <input type="file" name="file_upload">
    <input type="submit" value="Go">
</form>

the js code processing it might be

jQuery(document).ready(function(){
    var form_data = {};
    $(this).find('input').each(function(){
            form_data[this.name] = $(this).val();
        });
    $('#file_form').ajaxForm({
        url: ajax_url, // there on the admin side, do-it-yourself on the front-end
        data: form_data,
        type: 'POST',
        contentType: 'json',
        success: function(response){
            alert(response.message);
        }
    });
});

and a better example can be found on the plugin site.
The PHP side of things, WordPress wise, requires queuing the scripts

function q_scripts(){
    $src = plugins_url('js/file_upload.js', __FILE__);
    wp_enqueue_script('my_ajax_file_uploader_thing', $src, array('jquery', 'jquery-form'));
}
add_action('init', 'q_scripts');

the AJAX handler

function handle_file_upload(){
    check_ajax_referer('ajax_file_nonce', 'security');

    if(!(is_array($_POST) && is_array($_FILES) && defined('DOING_AJAX') && DOING_AJAX)){
        return;
    }

    if(!function_exists('wp_handle_upload')){
        require_once(ABSPATH . 'wp-admin/includes/file.php');
    }
    $upload_overrides = array('test_form' => false);

    $response = array();

    foreach($_FILES as $file){
        $file_info = wp_handle_upload($file, $upload_overrides);

        // do something with the file info...
        $response['message'] = 'Done!';
    }

    echo json_encode($response);
    die();
}

add_action('wp_ajax_my_file_upload', 'handle_file_upload');
// not logged in users might need love too
add_action('wp_ajax_nopriv_my_file_upload', 'handle_file_upload');

and that’s pretty much all of it in its most basic, unsecure, attack-prone form. There is a function for it.