Adding AJAX to your WordPress Plugins

This weekend I’m giving a talk at WordCamp Victoria about using AJAX in WordPress. The talk uses a couple of code snippets to demonstrate creating a simple plugin that passes some data from the client to the server and then updates the web page using data sent back from the server. Here’s the code for the final, most functional, plugin:

 

Firstly the PHP that runs on the server:

<?php
/*
Plugin Name: AJAX Demo 8
Description: A demonstration of how to use AJAX within WordPress
Author: Jon Jennings
Version: 1.0
Author URI: http://jonjennings.org/
*/
 
 
class ajax_demo8 {
 
 
	public function __construct() {
		add_shortcode('ajax-demo8', array($this, 'shortcode_callback'));
		wp_enqueue_script( 'ajax-demo8', plugin_dir_url( __FILE__ ) . 'ajax-demo.js', array( 'jquery', 'json2' ) );
		add_action('wp_ajax_ad_submit8', array($this, 'submit_callback'));
		add_action('wp_ajax_nopriv_ad_submit8', array($this, 'submit_callback'));
		add_action('wp_enqueue_scripts', array($this, 'enqueue'));
	}
 
 
	public function enqueue() {
		wp_localize_script( 'ajax-demo8', 'ajaxDemoData',
			array(
				'ajaxUrl' => admin_url( 'admin-ajax.php' ),
				'_nonce' => wp_create_nonce('ad-nonce')
			)
		);			
	}
 
 
	public function shortcode_callback($atts) {
 
		$ret = '<div id="ajax-demo">';
 
		$ret .= 	'<div id="ad-form">' .
					'<input type="button" name="send8" id="send8" value="click me" />' .
					'</div>';
 
		$ret .=		'<div id="ad-wait" style="display:none;">' .
					'Please wait' .
					'</div>';
 
		$ret .=		'<div id="ad-output" style="display:none;">' .
					'</div>';
 
		$ret .= '</div>';
 
		return($ret);
	}
 
 
	public function submit_callback() {
 
		$filename = sanitize_file_name( $_POST['filename'] );
		$nonce = $_POST['nonce'];
 
		// Warning - kinda dangerous
		$file = file_get_contents(plugins_url( $filename , __FILE__ ));
		$file_data = base64_encode($file);
 
		if (!wp_verify_nonce($nonce, 'ad-nonce')) {
			echo json_encode( array(
				'result' => 'error'
			));
 
		} else {
			echo json_encode(
					array(
						'image' => $file_data
					)
			);
		}
		exit;
	}
}
 
 
$ajax_demo8 = new ajax_demo8();
?>

And then the JavaScript that the client runs:

jQuery(document).ready(function ($) {
 
	$('#send8').click(function () {
		$("#ad-form").hide();
		$("#ad-wait").show();
 
		// url, data, success_callback
		// http://localhost/wordcamp/wp-admin/admin-ajax.php?action=ad_submit6&input=12312
		$.post(
			ajaxDemoData.ajaxUrl,
			{
				action: 'ad_submit8',
				filename: 'taf.gif',
				nonce: ajaxDemoData._nonce
			},
			ad_success
		);
	});
 
 
	function ad_success(a) {
		var data = JSON.parse(a);
 
		$('#ad-output').html('<img alt="Embedded Image" src="data:image/gif;base64,' + data.image + '" />');
		$('#ad-wait').hide();
		$('#ad-output').show();
	}
});

(You’ll need to supply your own GIF though!)

 

Edit: Note that if you actually just wanted to display the image in the browser, there’s no reason to load the image’s data into the server – all you’d need to do is to send the image’s URL to the client and have the client reference the URL rather than the data. The browser would then be responsible for loading the image from the web server without it ever being loaded into PHP there.

You should also note, as I mentioned in the presentation, that the code I showed live allows a client to request ANY, I repeat ANY, file off your server. So if the client asks for ‘../../../wp-config.php’ then it would give them a copy of your WordPress database details! This is not a good thing. The WordPress function sanitize_file_name() is your friend here – it’ll strip out any dodgy characters, including periods or slashes or asterisks, from a string. This means the client can no longer escape from your plugin’s directory. Just to be safe, I’ve updated the code here to use that. However the client could still request your plugin’s code, so if you needed to do something like this in a production environment you should also take measures to prevent that – eg set a naming strategy for the files you expect to be requested and check that it’s being followed.

Thirdly, Joey pointed out that embedding base64 encoded binary data in the browser is generally a bad thing. Base64 encoding is often used to hide malicious code (I should remember this point – I was hacked once with the code disguised in base64!) and so anything that uses it will be rejected by the WordPress repositories.

My purpose writing this part of the demo wasn’t to show best practices, it was more to show that you can use AJAX to transmit complex data to the client and not just a simple string or integer. That and a cute finish for my talk :-)

Within the next couple of days, I’ll add a post explaining the content in more detail.

4 comments to Adding AJAX to your WordPress Plugins