WordPress:AJAX in Plugin

来自站长百科
Xxf3325讨论 | 贡献2008年8月27日 (三) 10:24的版本 (新页面: ==Introduction== Ajax (Asynchronous JavaScript And XML) is a technology that allows a web page to update some of its information without a full page reload. It is used ...)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转至: 导航、​ 搜索

Introduction

Ajax (Asynchronous JavaScript And XML) is a technology that allows a web page to update some of its information without a full page reload. It is used in the Administration sections of WordPress for auto-save of post edits, adding new categories, and other purposes. Some WordPress WordPress:Plugins also use AJAX for things like voting on post ratings and map refreshes.

This article, aimed at plugin developers, describes how to add Ajax to a plugin. Before reading this article, you should be familiar with the following:

  • Ajax - Overview of the technology
  • WordPress:Writing a Plugin - How to write a plugin
  • WordPress:Plugin API - Filters and actions - what they are and how to use them
  • How to add HTML to the appropriate WordPress page, post, or screen -- for instance, if you want to add Ajax to administration screens you create, you will need to understand how to add administration menus to WordPress; if you want to add Ajax to the display of a single post, you'll need to figure out the right filters and actions to add HTML to that spot on viewer-facing blog screens. This article does not cover these topics.
  • You will also need to know something about client-side JavaScript programming, PHP programming, and HTML, in order to use Ajax in WordPress.

It turns out that the techniques for adding Ajax to a plugin are quite different, depending on whether you want the Ajax functionality to be part of the WordPress administration screens, or to appear on the viewer-facing side of WordPress. So, this article treats those two possibilities separately (after discussing common considerations).

Ajax Implementation Basics

There are three steps in an Ajax request, in general:

  1. The user does something (such as clicking or dragging the mouse), and JavaScript embedded in the HTML of the web page responds by composing a "request" and sending it to a special URL on the web server. Due to security restrictions, the URL the request goes to must be on the same web site that the file containing the JavaScript came from.
  2. A script or program on the web server (in WordPress, this will generally be one or more PHP functions) processes the request and sends information back to the browser.
  3. The returned information is displayed using JavaScript.

Unfortunately, much of Ajax is JavaScript, which runs in the user's web browser, and the different web browsers have not all implemented the Ajax calls and responses in the same manner. So, to make things easier, most Ajax developers choose to use a tested cross-browser library that wraps the particular browser idiosyncracies in a standard class with a documented API. In this article, we'll use one such library, SACK (Simple Ajax Code-Kit), which is included in WordPress. We'll need to make sure both the SACK library and the JavaScript functions that compose the Ajax request get included in the HTML head section of the web page where the Ajax request will take place; the sections below will show how to do that for both the administration and viewer-facing sides of WordPress.

When creating an Ajax request using the SACK library, we'll need to supply the following information; the sections below will fill in the details of what this information should be for the administration side and the viewer-facing side:

  • Request URL: The URL on the server that will process the Ajax request.
  • Custom request variables: SACK allows us to set arbitrary request variables, which are sent via POST or GET to the server. Cookie information can also be sent.
  • What to do if there is an error: a JavaScript function to call if there is an Ajax error.

By default, SACK assumes that the returned information from the server is JavaScript code, which is executed when it comes in (asynchronously). In the examples below, we'll use this default behavior, so the PHP functions that are processing Ajax requests will need to compose their results into JavaScript commands. If you want to do something else with the returned information in your plugin, you might want to visit the SACK Project Home Page, download the zip file, and read the documentation, because there are definitely other possibilities.

One other detail is that the PHP function that processes the Ajax request should use the PHP die function to pass back its JavaScript-encoded information. Example: die("javascript_commands_here")

With this common Ajax background in mind, the next two sections go through examples of how to use Ajax on the administration screens of WordPress, and on the viewer-facing side of WordPress. The two sections are independent, so you can just read the one that is appropriate for your plugin, if you want.

Ajax on the Administration Side

Since Ajax is already built into the core WordPress administration screens, adding more administration-side Ajax functionality to your plugin is fairly straightforward, and this section describes how to do it. As mentioned above, if you want to use Ajax on the blog-viewer-facing side of WordPress, you can completely skip this section of the article.

As a simple example of using Ajax on the administration screens of a WordPress plugin, consider a geographical tagging plugin, where the user enters the latitude and longitude for a post, and the plugin will look up the elevation at that point, using a web service. Before we even think about Ajax programming, we need some fields on the screen where the user can enter the latitude and longitude, a button to look up elevation, and an elevation field to display the result. This article assumes you already know how to add these to the appropriate screen, and can adjust field widths, text, styles, etc. to your liking; you'll want to add something like this, inside an HTML form in the admin screens:

Latitude: <input type="text" name="latitude_field" />
Longitude: <input type="text" name="longitude_field" />
<input type="button" value="Look Up Elevation" 
onclick="myplugin_ajax_elevation(this.form.latitude_field,this.form.longitude_field,this.form.elevation_field);" />
Elevation: <input type="text" name="elevation_field" id="elevation_field" />

Next, we need to define the JavaScript function myplugin_ajax_elevation, the onClick action for the button, which will read the information the user entered, compose a request with SACK, and send it to the plugin for processing. As mentioned in the previous section, this JavaScript function and the SACK library need to get added to the HTML head section of the administration screen in question; the easiest way to do that is to add it to all admin screens using the admin_print_scripts action:

add_action('admin_print_scripts', 'myplugin_js_admin_header' );

function myplugin_js_admin_header() // this is a PHP function
{
  // use JavaScript SACK library for Ajax
  wp_print_scripts( array( 'sack' ));

  // Define custom JavaScript function
?>
<script type="text/javascript">
//<![CDATA[
function myplugin_ajax_elevation( lat_field, long_field, elev_field )
{
    // function body defined below

} // end of JavaScript function myplugin_ajax_elevation
//]]>
</script>
<?php
} // end of PHP function myplugin_js_admin_header

The next step is to fill in the body of the myplugin_ajax_elevation JavaScript function, which is supposed to read the latitude and longitude from the form fields, compose an Ajax request with SACK, and send the request to the server. Given the list of generic SACK information from the previous section, here is what we need to set up for this example:

  • Request URL: We're going to send our request to a special URL that is defined in the administration menu system of WordPress: (bloghome)/wp-admin/admin-ajax.php. Below, we'll see how to add a special Ajax action to WordPress that will tell that script which plugin PHP function to call when the request is received. For our purposes, let's assume the action is called "myplugin_elev_lookup".
  • Custom request variables: We need to send the latitude and longitude to the server; we also need to send our "action" name to the admin-ajax.php script. We also need to send the current page's cookies, since they contains the login information. And finally, since our server-side function will need to return JavaScript to display the results, we'll need to pass the ID of the elevation field into our function.

Putting it all together, the body of the JavaScript function becomes:

function myplugin_ajax_elevation( lat_field, long_field, elev_field )
{
   var mysack = new sack( 
       "<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php" );    

  mysack.execute = 1;
  mysack.method = 'POST';
  mysack.setVar( "action", "myplugin_elev_lookup" );
  mysack.setVar( "latitude", lat_field.value );
  mysack.setVar( "longitude", long_field.value );
  mysack.setVar( "elev_field_id", elev_field.id );
  mysack.encVar( "cookie", document.cookie, false );
  mysack.onError = function() { alert('Ajax error in looking up elevation' )};
  mysack.runAJAX();

  return true;
} // end of JavaScript function myplugin_ajax_elevation

The final step in this example is to define what happens when the Ajax request gets to the server. As mentioned above, we are sending the request to (bloghome)/wp-admin/admin-ajax.php, with an "action" parameter of "myplugin_elev_lookup". We'll use the wp_ajax_* action to tell WordPress which PHP function in our plugin to call when it receives this request.

Our PHP function will need to send the latitude and longitude to the elevation lookup server, wait for a response, parse the result, and send the information back as JavaScript commands. We'll use the "Snoopy" web request PHP class (built into WordPress) to send the web request. Here's how to do it:

add_action('wp_ajax_myplugin_elev_lookup', 'myplugin_ajax_elev_lookup' );

function myplugin_ajax_elev_lookup()
{
  // read submitted information

  $lat = $_POST['latitude'];
  $long = $_POST['longitude'];
  $field_id = $_POST['elev_field_id'];
  $units = "FEET";

  // Build Snoopy URL request

  require_once( ABSPATH . WPINC . '/class-snoopy.php' );
  $sno = new Snoopy();
  $sno->agent = 'WordPress/' . $wp_version;
  $sno->read_timeout = 2;
  $reqURL = "http://gisdata.usgs.gov/XMLWebServices/TNM_Elevation_Service.asmx/getElevation?Y_Value=$lat&X_Value=$long&Elevation_Units=$units&Source_Layer=-1&Elevation_Only=1";

  // Send request to elevation server 
  if( !$sno->fetchtext( $reqURL )) {
    die( "alert('Could not connect to lookup host.')" );
  } 

  // Parse response
  if( !preg_match("|<Elevation>([\d.-]+)</Elevation>|",$sno->results)){ 
     die( "alert('Could not find elevation in lookup results.')" );
  } 
 
 $matches=preg_split( "|<Elevation>([\d.-]+)</Elevation>|",$sno->results); //REGEX BUG: but it'll return info

  // Compose JavaScript for return
  die( "document.getElementById('$field_id').value = " .
       $matches[1] );
} // end of myplugin_axax_elev_lookup function

That's it! You will need to add a few details, such as error checking and verifying that the request came from the right place, but hopefully the example above will be enough to get you started on your own administration-side Ajax plugin.

Ajax on the Viewer-Facing Side

Implementing Ajax on the viewer-facing side of a WordPress site is slightly more ad-hoc than doing Ajax on the administration side, because WordPress itself doesn't have viewer-facing Ajax built in, but it's not too bad, and this section describes how to do it. As mentioned above, if you want to use Ajax on the administration side of WordPress, you can completely skip this section of the article.

As an example, let's consider a plugin that allows a blog viewer to vote on or rate something (which could be a generic poll plugin, a post rating plugin, or something else like that). When the blog viewer submits a vote, we want the submission to go via Ajax, so that the viewer doesn't have to wait for the page to refresh; after the vote is registered, we'll want to update the running vote total display. For this example, we'll assume the voting and display are text-based, for simplicity, and we'll assume you've either edited the theme or used a WordPress filter or action to add HTML to the appropriate WordPress viewer-facing screen. The added HTML will look something like this:

<form>
Your vote: <input type="text" name="uservote" />
<input type="button" value="Vote!" onclick="myplugin_cast_vote(this.form.uservote,'voteresults');" />
<div id="voteresults">
(previous results output from your PHP function)
</div>
</form>

Next, we need to define the JavaScript function myplugin_cast_vote, the onClick action for the button, which will read the information the user entered, compose a request with SACK, and send it to the plugin for processing. As mentioned in the introductory section, this JavaScript function and the SACK library need to get added to the HTML head section of the web page the user is viewing. One way to do that is to add it to all viewer-facing screens using the wp_head action (you could also be a little more selective by using some of the is_xyz() WordPress:Conditional Tags tests):

add_action('wp_head', 'myplugin_js_header' );

function myplugin_js_header() // this is a PHP function
{
  // use JavaScript SACK library for Ajax
  wp_print_scripts( array( 'sack' ));

  // Define custom JavaScript function
?>
<script type="text/javascript">
//<![CDATA[
function myplugin_cast_vote( vote_field, results_div )
{
    // function body defined below

} // end of JavaScript function myplugin_cast_vote
//]]>
</script>
<?php
} // end of PHP function myplugin_js_header

The next step is to fill in the body of the myplugin_cast_vote JavaScript function, which is supposed to read the vote from the form field, compose an Ajax request with SACK, and send the request to the server. Given the list of generic SACK information from the introductory section, here is what we need to set up for this example:

  • Request URL: We need to send our request to a plugin PHP file. This could be the main plugin PHP file, or a separate PHP file. It's probably a little cleaner to do it in a separate file, which we'll assume is called "myplugin_ajax.php", located in the standard wp-content/plugins directory of WordPress.
  • Custom request variables: We only need to send the vote and the ID of the div where the results go to the server, in this simple example.

Putting this together, the body of the JavaScript function becomes:

function myplugin_cast_vote( vote_field, results_div )
{
   var mysack = new sack( 
       "<?php bloginfo( 'wpurl' ); ?>/wp-content/plugins/myplugin_ajax.php" );    

  mysack.execute = 1;
  mysack.method = 'POST';
  mysack.setVar( "vote", vote_field.value );
  mysack.setVar( "results_div_id", results_div );
  mysack.onError = function() { alert('Ajax error in voting' )};
  mysack.runAJAX();

  return true;

} // end of JavaScript function myplugin_cast_vote

The final step in this example is to define what happens when the Ajax request gets to the server. This will go into the myplugin_ajax.php file, which will need to read the posted information, verify that the vote is valid, add the vote to the database, figure out what the new running totals are, and send that back to the browser. Leaving aside all the details of processing the vote, here's what needs to go into the file:

<?php

// Put your author and license information here
// TO DO!

// Check request came from valid source here
// TO DO!

// read submitted information

$vote = $_POST['vote'];
$results_id = $_POST['results_div_id'];

// Put your vote processing code here
// In this example, assume 
//  a) The processing code sets global
// variable $error to a message if there is an error
//  b) If there is no error, $results contains
// the HTML to put into the results DIV on the screen

if( $error ) {
   die( "alert('$error')" );
} 

// Compose JavaScript for return
die( "document.getElementById('$results_id').innerHTML = '$results'" );

?>

That's it! You will need to add a few details, such as error checking, escaping quotes, processing the vote, and verifying that the request came from the right place, but hopefully the example above will be enough to get you started on your own viewer-side Ajax plugin.