wordpress and ajax · first things first... what the heck is ajax anyways besides a lovely acronym...

252

Upload: others

Post on 10-Jul-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner
Page 2: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WORDPRESS & AJAXAn in-depth guide on using Ajax with WordPress

RONALD HUERECA

cover & book design by Vivien Anayian

Page 3: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax

Copyright © 2010 by Ronald Huereca

All Rights Reserved under International and Pan-American Copyright Conventions

This book is protected under U.S. and international copyright law. No part of this book may be used or reproduced in any manner without the written permission of the copyright holder except where allowed under fair use.

The rights of this work will be enforced to the fullest extent pos-sible, including under the Digital Millennium Copyright Act of 1998 (DMCA) of the United States, the European Directive on Electronic Commerce (EDEC) in the European Union and other applicable U.S. and international laws.

All source code used in this book is licensed under the GNU Public License (GPL). For more information about the GPL or to obtain a copy of the full license, please visit http://www.gnu.org/copyleft/gpl.html.

BOOK DESIGNERVivien Anayian

COVER DESIGNERVivien Anayian

COVER ILLUSTRATIONSAntelope by Haekel, Vintage educational plate Killer Whale by W. Sidney Berridge from A Book Of Whales, 1900. Plate XXI (From cast in National History Museum)

PRIMARY TYPEFACESAdobe Garamond Pro, designed by Claude Garamond, Robert Slimbach Futura, designed by Paul Renner Myriad Pro, designed by Robert Slimbach, Carol Twombly Monaco, designed by Susan Kare, Kris Holmes, Charles Bigelow

Page 4: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Table of ContentsAcknowledgments ................................................9About the Author 10Credits and Thanks 11

Introduction .......................................................13How About a Little Ajax? 14Prerequisites For This Book 15

Chapter 1What is Ajax? .....................................................17

Chapter 2Adding Scripts Properly to WordPress ................23So How Does wp_enqueue_script Work? 25

Handle 25Src 26Deps 26Ver 27

In_footer 28Great, I have wp_enqueue_script down. Now what? 29

Page Detection 32Loading Only Your Scripts 34Creating Standalone Pages 35Adding Scripts Conclusion 40

Page 5: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 3Localizing Your Scripts .......................................43wp_localize_script 45

Handle 45Object_name 45

l10n 46wp_localize_script Example 46Other Localization Techniques 48The JSON Technique 48A Custom Function 50Localization Conclusion 51

Chapter 4Properly Formatting jQuery for WordPress Use ..53Namespacing 54Public Properties/Functions 56Private Variables/Functions 57Conclusion for Properly Formatting jQuery for WordPress 60

Chapter 5Preventing Plugins From Loading.......................61

Chapter 6Sending Our First Ajax Request .........................65

Page 6: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Set Up the PHP Class to Handle Back-end Operations 67Setting up the Interface 68Setting Up the JavaScript File 71Setting up the Ajax Object 74Finalizing the functions.php Class 75

The get_comments Method 76Add in our JavaScript Files 76Add in JavaScript Localization 78Add in String Localization 81Add in Query Variable Support 82The Finalized functions.php Class 83

Finalizing the Ajax Request 85

Chapter 7Server-Side Security for Ajax .............................89Loading the WordPress Environment 90Performing a Nonce Check 91

Chapter 8Server-Side Processing of Ajax Requests ..............95

Chapter 9Server-Side Responses of Ajax Requests ..............99

Page 7: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 10Client-Side Processing/Parsing .........................105Processing the Data 108

Chapter 11The Output ......................................................113

Chapter 12Example #1: WP Grins Lite ............................123The WPGrins Class 127

The Constructor 128add_scripts 130get_js_vars 132wp_grins_head 134add_admin_pages 135print_admin_page 135get_admin_options 136save_admin_options 138wp_grins 138Add in Query Variable Functionality 140Our Template Tag 141

The Admin Panel 142The JavaScript File (wp-grins.js) 147The Ajax Processor 151

Page 8: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13Example #2: Static Random Posts ...................153Creating the Static Random Posts Widget 156

form 158update 161get_admin_options 162save_admin_options 163init 164add_admin_pages 165print_admin_page 165add_post_scripts 166get_js_vars 167Setting Up Our Query Variables 168widget 169get_posts 173build_posts 176print_posts 177

The Admin Panel 179Updating the Options 180The User Interface 183The JavaScript File (static-random-posts.js) 187The Ajax Processor 194Static Random Posts Conclusion 198

Page 9: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14Example #3: Dynamic PayPal Buy Now Options With Coupon Codes ........................................199Setting Up the User Interface 202Adding Our Script File 206Processing Our Ajax Request 217

Setting Up a Private and Public Certificate With PayPal 221Setting up a Connection to PayPal via OpenSSL 226

Setting up a Simple Instant Payment Notification (IPN) Script 235Example Conclusion 241

Chapter 15Reduce Your JavaScript Footprint .....................243W3 Total Cache 244Script Compression 246Conclusion 249

Page 10: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Acknowledgments

Page 11: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax10

About the AuthorPeople describe Ronald as a highly opinionated (and sometimes funny) writer.

He’s worked with WordPress since 2006 and has released several WordPress plugins, his most pop-ular being Ajax Edit Comments.

Ronald currently calls Austin, Texas home. His education includes a Master’s in Business Admin-istration and a degree in Electronics Engineering Technology.

When he’s not killing trees or reindeer, he blogs at his personal site, ronalfy.com.

Page 12: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Acknowledgments 11

Credits and ThanksFirst, I would like to thank my family, friends, and God for getting me through a very tough per-sonal situation in 2009. Without them, this book would not have been possible.

I’d like to thank Vivien Anayian for the inspira-tion behind Ajax Edit Comments, which conse-quently started me with Ajax and WordPress.

I’d also like to thank Ajay D’souza and Mark Ghosh. Both have helped me tremendously along my WordPress journey.

Finally, I’d like to thank the folks at Automattic and the contributors behind WordPress.

Page 13: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner
Page 14: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Introduction

Page 15: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax14

How About a Little Ajax?Perhaps I’m a little biased, but I love Ajax.

Ajax is what helps achieve that “rich” Internet ex-perience. Granted, there are those that misuse Ajax, but that’s what comes with every piece of technology; there are those that will use and abuse (and they should be spanked unmercilessly).

After scanning article after article on WordPress, I have yet to find one that adequately covers how to achieve the Holy Grail of Ajax with WordPress.

The WordPress and Ajax book began humbly. It began as a series of articles on how to use Ajax with WordPress. However, my outline for the se-ries soon ballooned and I realized that the topic of WordPress and Ajax cannot be covered by a mere series of articles. In order to adequately cover the topic, there needed to be much, much more.

The goal of this book is to provide you a rock-solid foundation for using Ajax with WordPress. After the foundation has been laid, you will be walked through several real-world examples. By the end of the book, you should not only have a thorough understanding of Ajax, but how Ajax functions within WordPress itself.

Page 16: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Introduction 15

The code examples I present are from my own experience with using Ajax, and you can expect more techniques and code examples to be added as the book matures.

Prerequisites For This BookI’m assuming the following if you intend to fol-low this book:

You are using the latest version of • WordPress (versions 2.8 and above should be accept-able).

You have “some” knowledge of • JavaScript and some knowledge of the uber-awesome jQuery library (you’re free to disagree with me on this, but keep in mind I carry a big stick).

You have some experience with • WordPress theme or plugin development.

Or, you have none of the above, but want to •read anyway (you’re the one that ignores the “falling rocks” signs on the highway, right?)

Disclaimer: my writing style has been known to cause brain hemorrhages, so please do not read this book if you smoke, are nursing, are on the pill, or are pregnant.

You have been warned, so let’s dive in!

There is no smoking while reading this book. You may spontaneously combust!

Page 17: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax16

Oh, wait! You might also want to use these two (invaluable) tools along the journey:

Firebug for Firefo• x: this tool is one of those where you wonder how you ever got along without it. It’ll assist you with debugging any JavaScript errors.

XAMP• P: this tool allows you to host WordPress locally. It’s great for testing scripts and themes without modifying a production install. Here’s a neat tutorial explaining how to install Word-Press using XAMPP. The tutorial is for Win-dows, but Mac users should be able to figure out the differences.

Page 18: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 1

What is Ajax?

Page 19: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax18

What is Ajax?First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner (at least, not on the Internet).

Let’s just say that Ajax consists of a bunch of tubes... Okay, bad joke, but let’s go on.

Ajax, in simple terms, allows a user on the client-side (the web browser) to indirectly interact with the server-side (the back-end that you will never see).

Ajax allows for complex processing of data that would be impossible to accomplish on the client-side.

Ajax uses JavaScript to send a request to the server, and the server (if needed) sends a response back.

It’s really that simple. Perhaps too simple for you techno-geeks, but I digress. I mean, I could go deep into technical mumbo-jumbo, but all it will get you is weird stares from the people you’re ex-plaining it to (and perhaps a slap from the girl you’re trying to pick up when you start talking about sending and receiving requests).

Page 20: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 1, What is Ajax? 19

Now implementing Ajax, and implementing it well, is another story. So let’s read on.

Ajax Best PracticesAs with any technology, Ajax can easily be abused. I’ve seen too many sites that depend on Ajax for navigation or to access crucial and/or critical con-tent without alternatives.

From my perspective, Ajax should be absolutely transparent and unobtrusive to the user. I’m thinking of Flickr and Netflix when I say this. Both sites mentioned use Ajax to add to the user experience, rather than detract from it.

When you click on that caption or description, or leave that star rating, it’s simple and to the point. All we should know from the end-user’s per-spective is that it works (and that’s all we should know).

Hitting the back-button doesn’t ruin the experi-ence; it just means you’re “done”.

I’ll leave Ajax best practices to just one statement: it should be absolutely and completely transpar-ent to the user. The user shouldn’t even know he’s using Ajax.

All we should know from the end-user’s perspective is that it works

Page 21: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax20

Ajax should be used to accomplish quick and sim-ple tasks. That’s it. Anything more is just asking to be beat down by the Internet troll (and he’s mean).

Here are some good examples of when to use Ajax:

Reduce the need to move away from the cur-•rent page. I’m thinking of Netflix’s star rating and “Add to Queue” features. The user gets to keep browsing and isn’t interrupted.

Allow quick editing of options. Observe how •easy it is to edit captions and descriptions in Flickr.

Allow quick functionality without a page re-•fresh. I’m thinking of WordPress’ use of com-ment moderation functions that allow you to stay on the same page without having to re-fresh.

And here are some bad examples:

Providing multi-page Ajax operations. If the •user hits the back button or refreshes, then what? He has to start all over from scratch (and he’s about to call the Internet troll on you).

Page 22: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 1, What is Ajax? 21

Loading content on websites. Ever been to one •of those sites that load the content when you click on a button or drop-down menu option? Not good. There’s no back button function-ality, no way to bookmark the content, and if you refresh, you’re back at the beginning. In other words, they might as well have used Flash (ducking for cover).

Using Ajax upon a page load. This last one is •more of a guideline as there are many excep-tions. Notable exceptions include scripts that track statistics and would like to avoid caching plugins.

Remember, keep Ajax simple. If the user realizes you are using it, you have failed.

Ajax should be absolutely and completely transparent to the user

Page 23: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner
Page 24: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2

Adding Scripts Properly to WordPress

Page 25: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax24

Adding Scripts Properly to WordPressStarting in WordPress 2.1 (if I remember correctly), the awesome folks at Automattic gave us the even awesomer function of wp_enqueue_script.

Before that, it was every plugin or theme author for himself. If you wanted to add in a script, it was hard-coded in.

As you might imagine, this presented a ton of problems. Scripts were loaded twice, out of order, or even when they weren’t needed at all.

Furthermore, some themes and plugins had the JavaScript embedded within the plugin’s or theme’s PHP file just to capture a few PHP variables. Not good! In order to add scripts properly to JavaScript, you must always keep your PHP and JavaScript separate. And by separate, I mean sep-arate files. There’s just no excuse anymore (I’ll get into this more when I cover localizing scripts).

The wp_enqueue_script function is the first step in loading your scripts properly. Not only can you add your script, but you can also specify the de-pendencies (e.g., jQuery), and the version num-ber.

Always keep PHP and JavaScript separate.

Page 26: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2, Adding Scripts Properly to WordPress 25

This prevents duplicate JavaScript files from load-ing, which is always a good thing.

In addition to covering wp_enqueue_script, I will cover a few advanced topics such as page detec-tion, loading just your script files, and creating standalone pages.

So How Does wp_enqueue_script Work?Aside from the excellent documentation from WordPress, you’ll find my regurgitated version be-low (gross, I know).

The function wp_enqueue_script takes in five argu-ments, with the last four being optional.

Handle

The handle argument is a string that names your script, or references someone else’s.

For example, you can add the jQuery script to any page by calling:

<?php wp_enqueue_script('handle', 'src', 'deps', 'ver', 'in_

footer'); ?>

wp_enqueue_script('jquery');

Page 27: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax26

SrcThe src argument is asking for the URL of the file. If you supply the src of the script, the wp_enqueue_script function automatically registers your script for others to use (no need to use wp_register_script).

An example of it in use is:

Likewise, if you have previously registered your script (using wp_register_script), you can call your script by calling:

wp_enqueue_script('my_script');

wp_enqueue_script('my_script', WP_CONTENT_URL . 'plugins/my_plugin/my_script.js');

Themers would use:

wp_enqueue_script('my_script', WP_CONTENT_URL . 'themes/my_theme/my_script.js');

DepsThe deps argument is an array of dependencies. For example, if your script requires jQuery or oth-

Page 28: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2, Adding Scripts Properly to WordPress 27

er JavaScript files, it’ll load these files before your plugin’s JavaScript file.

Here’s an example:

See that array up there (it’s pretty, I know)? That’s what’s calling your dependencies.

jQuery is built into WordPress by default, so calling it via dependency is no problem as long as you queue it via wp_enqueue_script.

The another_script dependency assumes you have already used wp_enqueue_script to assign it a han-dle and a src.

The outcome in this scenario is that jQuery and another_script will load before my_script.

VerThe ver argument is simply the version of the JavaScript file for caching purposes. If you are supplying your own script, it’s a good idea to sup-ply a version.

wp_enqueue_script('my_script', WP_CONTENT_URL . 'plugins/my_plugin/my_script.js', array('jquery', 'another_script'));

Page 29: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax28

As you update your JavaScript file, update the ver argument. If you don’t, you’ll inevitably run into caching issues with WordPress.

In_footerThe in_footer argument (since WP 2.8) deter-mines if the script will load in the header or footer (header is the default).

To load your script in the footer, simply pass it a 1 (or true).

The version is string based and looks like this:

wp_enqueue_script('my_script', WP_CONTENT_URL . 'plugins/my_plugin/my_script.js', array('jquery', 'another_script'), '1.0.0');

wp_enqueue_script('my_script', WP_CONTENT_URL . 'plugins/my_plugin/my_script.js', array('jquery', 'another_script'), '1.0.0', true);

Great care should be used when setting the scripts to load in the footer as some themes may not have this functionality built in (the same problem ex-ists for loading scripts in the header).

Page 30: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2, Adding Scripts Properly to WordPress 29

Great, I have wp_enqueue_script down. Now what?You have to call wp_enqueue_script from the appro-priate hook in WordPress.

Fortunately, WordPress has two such action hooks you can tap into: admin_print_scripts and wp_print_scripts.

The admin_print_scripts hook allows you to add scripts in the admin panel, while the wp_print_scripts hook allows you to add scripts everywhere else.

Adding your wp_enqueue_script code is as simple as adding an action inside your plugin or theme code.

An example of the code being used inside of a class constructor is:

add_action('admin_print_scripts', array(&$this, 'add_admin_scripts'));

And for those not using a class (classes help avoid conflicts with other plugins):

Page 31: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax30

<?php /* Plugin Name: My Plugin Plugin URI: http://www.myplugin.com/ Description: Sample script Author: You Version: 1.0 Requires at least: 2.9 Author URI: http://www.yourdomain.com/ */ if (!class_exists('myPlugin')) { class myPlugin { /** * PHP 4 Compatible Constructor */ function myPlugin(){$this->__construct();} /** * PHP 5 Constructor */ function __construct() { if ( !defined('WP_CONTENT_URL') ) define( 'WP_CONTENT_URL', get_option('siteurl') . '/wp-content'); if ( !defined('WP_CONTENT_DIR')) define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' ); //JavaScript add_action('wp_print_scripts', array(&$this,'add_scripts'),1000); } //end constructor //Adds the appropriate scripts to WordPress function add_scripts(){ wp_enqueue_script('another_script', WP_

add_action('admin_print_scripts', 'add_admin_scripts');

Here’s an example class:

Page 32: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2, Adding Scripts Properly to WordPress 31

CONTENT_URL . 'plugins/my_plugin/another_script.js', '', '1.0.0'); wp_enqueue_script('my_script', WP_CONTENT_URL . 'plugins/my_plugin/my_script.js', array('jquery', 'another_script'), '1.0.0'); }//End add_scripts }//End class myPlugin }//End if class

//Further avoid conflict if (class_exists('myPlugin')) { //instantiate the class $mypluginvar = new myPlugin(); }

?>

You’ll notice that in the __construct method that the constant WP_CONTENT_URL is defined for use in our eventual plugin path. You’ll also notice that our action wp_print_scripts is declared for the method add_scripts.

Once you venture into the add_scripts method, you’ll see the use of the wp_enqueue_script function in all its glory.

For themers, just include the class in your func-tions.php file and replace the wp_enqueue_script src argument with the code such as shown next:

Page 33: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax32

Page DetectionYou might find yourself in the situation where you only want a script to run on a certain page. In fact, it’s good practice to only load your JavaScript files when absolutely necessary; loading the files on every single page is a big no-no (I’ve been chas-tised before for this).

While on the blog’s front-end, WordPress makes it super-easy with its conditional tags.

I’m not going to go over the conditional tags here, but here are a few you can take advantage of:

is_home(• )

is_front_page(• )

is_single(• )

is_page(• )

And • much more.

While being selective on the front-end is relatively straightforward, the admin-panel is another mon-ster.

wp_enqueue_script('my_script', WP_CONTENT_URL . 'themes/my_theme/my_script.js');

Page 34: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2, Adding Scripts Properly to WordPress 33

Sure, there’s the is_admin() conditional, but what if you only want to run a script in a certain sec-tion?

One technique is to use the PHP reserved variable called $_GET.

Say you have a page with a URL of:

http://www.mydomain.com/wp-admin/options-general.php?page=my-plugin-file.php

Use page detection to load scripts only where needed.

You can use the $_GET variable to capture the page and run a conditional to determine if the page is yours.

<?php if (isset($_GET['page'])) { if ($_GET['page'] == "my-plugin-file.php") { /*load your scripts here */ } } ?>

If there are no variables to capture, you may have to use the reserved variable $_SERVER to capture the page name. From there, you would use a condi-tional or a switch statement.

Page 35: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax34

<?php switch (basename($_SERVER['SCRIPT_FILENAME'])) { case "edit-comments.php": /*load scripts here*/ break; case "upload.php": /*load other scripts here*/ break; default: break; } ?>

Both code snippets shown above would run within your add_scripts method shown in the sample class example. Also note that you’ll need to use the admin_print_scripts action when in the admin panel.

Loading Only Your ScriptsFor advanced users, there may be occasions where you only want to load just your scripts. You would do this in order to minimize potential conflicts with other script files.

Situations where you may want to do this are:

On certain page templates.•

When using • ThickBox or Colorbox as an inline frame.

For themers, doing this is relatively simple. Just re-move the wp_head() action from your header.php file.

Page 36: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2, Adding Scripts Properly to WordPress 35

From there, you queue your script, and call the wp_print_scripts() function with the name of your script file.

Here’s some example code:

<?php /*Use variants of this code before the </head> tag in your header.php file*/ wp_enqueue_script('my_script', WP_CONTENT_URL . 'themes/my_theme/my_script.js', array('jquery'), '1.0.0'); wp_print_scripts(array('my_script')); ?>

We simply queue our scripts, and then pass an array of our script name to the wp_print_scripts function. This ensures that only our scripts will load.

I would use this technique rather sparingly, as re-moving the wp_head() function from your theme will cause a lot of plugins to crash and burn, but there are some instances where it is much needed such as with client work.

Creating Standalone PagesFor those needing to create standalone pages out-side of the normal templating system that Word-Press provides, you will first need to manually load the WordPress environment.

Page 37: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax36

Uses for this technique include:

Providing help pages for plugins or themes.•

Providing an Ajax processor for your script file •(more on that later).

Providing a manually created page (not a •permalink) that needs WordPress as a frame-work.

We’ll first go over how to call wp-load.php directly. However, there is a more elegant way, which I will go over next.

Here’s some example code:

<?php /*The use of the dirname functions depend on the hierarchy of your file. Adjust them as needed.*/ //Code should be used before the </head> tag of your file. $root = dirname(dirname(dirname(dirname(dirname(__FILE__))))); if (file_exists($root.'/wp-load.php')) { // > WP 2.6 require_once($root.'/wp-load.php'); } else { // < 2.6 require_once($root.'/wp-config.php'); } wp_enqueue_script('my_script', WP_CONTENT_URL . 'themes/my_theme/my_script.js', array('jquery'), '1.0.0'); wp_print_scripts(array('my_script')); ?>

Page 38: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2, Adding Scripts Properly to WordPress 37

Please note that the $root variable will have to be modified depending on where your page lies in the directory structure. You may have to use more, or less, dirname function calls (trial and error is your good, albeit always drunk, friend).

This technique assumes you know where wp-load.php is. If you’re working on a client site, chances are you know where it is.

If you’re a plugin or theme author writing for the masses, however, the location is unpredictable.

This is because users can choose to move their wp-content directory whereever they choose. Since your theme and/or plugin resides in this directory, finding wp-load.php is at best a guessing game.

The more elegant way to accomplish standalone pages is through query variables.

First, let’s begin by setting up one action and one filter and pointing them to some helper func-tions.

<?php add_action('template_redirect', 'load_page'); add_filter('query_vars', 'query_trigger'); ?>

Page 39: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax38

Themers would place the above code in your func-tions.php file, while plugin authors would include it in their base plugin file. Keep in mind I made the function names rather generic. It’s up to you to come up with something more unique in order to avoid conflicts.

The query_vars filter enables us to add a query variable to WordPress. The template_redirect ac-tion allows us to capture this query variable and perform an action on it.

Let’s start with the query_trigger function first.

Trial and error is your good, albeit always drunk, friend.

With the query_trigger function, we are automatically passed a list of the existing WordPress query variables. It’s our job to add in our query (in this case, my_page) and return the $queries array.

Now we have to do something with our query vari-able, so let’s move on to the load_page function.

function query_trigger($queries) { array_push($queries, 'my_page'); return $queries; }

function load_page() { $pagepath = WP_CONTENT_DIR . '/plugins/my-plugin-dir/'; switch(get_query_var('my_page')) {

Page 40: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2, Adding Scripts Properly to WordPress 39

The first thing the function does is establish an include path, which is captured in the $pagepath variable.

The function then performs a switch statement in order to capture the query variable (if applicable).

Right now, we have two cases: one for help.php and another for changelog.php. If any of those are found, we load the appropriate page.

So here’s how it works. Instead of pointing your browser to a WordPress page, you would point it to a query.

Using http://mydomain.com/?my_page=help.php would load the help.php file.

Using http://mydomain.com/?my_page=changelog.php would load changelog.php.

case 'help.php': include($pagepath . 'help.php'); exit; case 'changelog.php': include($pagepath . 'changelog.php'); exit; default: break; } }

Page 41: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax40

The benefits of this technique? No having to search for wp-load.php. You also have access to all the available WordPress functions, classes, actions, filters, and so on.

Please note in both help.php and changelog.php, you will have to queue your scripts like in the wp-load example shown earlier.

So in order to load scripts on a standalone page, you will need to:

Load the WordPress environment (via • wp-load.

php or using query variables).

Queue your scripts for later inclusion.•

Call the • wp_print_scripts function to load your scripts.

Adding Scripts ConclusionHere are the steps you should follow in order to add scripts to a WordPress theme or plugin:

Add the appropriate action to your WordPress •theme or plugin (themers will want to use functions.php).

Determine your script dependencies.•

Code your • wp_enqueue_script calls appropri-ately.

Page 42: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 2, Adding Scripts Properly to WordPress 41

Advanced users can also use page detection, •selectively load scripts, and create standalone pages.

One last thing (err, warning?) to take note of is naming your handler for wp_enqueue_script. If you are using a third-party script (say, Colorbox), use common sense in naming the handler. There’s no reason to name it “lightbox” or “mybox”. The handler name should be obvious, right? If you still don’t get it, I will hire Jay and Silent Bob to pay you a visit.

That’s it! So now you have no excuse not to load scripts appropriately (if you do, the Internet troll will get you).

Page 43: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax42

Figure 1. The Internet Troll is looking for you,

and he’s mean!

Page 44: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 3

Localizing Your Scripts

Page 45: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax44

Localizing Your ScriptsWhen adding scripts to WordPress, you will inevi-tably run into a small, but painful, issue of local-ization.

Localizing a plugin or theme is relatively straight-forward, but JavaScript presents its own difficul-ties since we can’t easily call the PHP functions necessary (which is one reason authors embed JavaScript in PHP files).

Since embedding JavaScript in PHP files is never a good technique, we use localization to save the day.

When using Ajax with WordPress, the JavaScript file will need to know the exact location of the WordPress site it is to interact with. You can’t hard-code this in unless you embed, so how does one get the location?

Furthermore, if you display anything with JavaScript, chances are your users will want the strings to be localized.

Fortunately, WordPress provides the ultra-handy wp_localize_script function.

Page 46: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 3, Localizing Your Scripts 45

wp_localize_scriptThe wp_localize_script takes three arguments:

handle•

object_name•

l10n•

HandleThe handle argument will be the same handle you use for your script name.

For example, if you have a handle of my_script, you would use the same name when calling the wp_localize_script function.

Object_nameThe object_name argument is a string that tells WordPress to create a JavaScript object using the name you specify.

It’s important that the string you pass is as unique as possible in order to minimize naming conflicts with other scripts.

For the upcoming example, our object name will be my_unique_name.

Page 47: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax46

l10nThe l10n argument is an array of strings you would like to localize.

Within this array, you will want to take advantage of the __ function.

wp_localize_script ExampleFor the purpose of this example, let’s create a function called localize_vars and have it return an array.

<?php function localize_vars() { return array( 'SiteUrl' => get_bloginfo('url'), 'OtherText' => __('my text', "my_localization_name") ); } //End localize_vars ?>

Please note the use of the __() function. It takes in the text we want to localize, and our localization name. This will be the same name you use if you take advantage of localization within WordPress.

The variable SiteURL gives us the http path to our WordPress site.

Page 48: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 3, Localizing Your Scripts 47

From another area in our code, we call the local-ize_vars function:

WordPress then conveniently adds localization JavaScript immediately before our main script is included. Viewing the page source will reveal:

<?php wp_enqueue_script('my_script', WP_CONTENT_URL . 'plugins/my_plugin/my_script.js', array('jquery'), '1.0.0'); wp_localize_script( 'my_script', 'my_unique_name', localize_vars()); ?>

<script type='text/javascript'> /* <![CDATA[ */ my_unique_name = { SiteUrl: "http://www.mydomain.com", OtherText: "my localized text" } /* ]]> */ </script> <script type='text/javascript' src='http://www.mydomain.com/wp-content/plugins/my_plugin/my_script.js?ver=1.0.0'></script>

With the localize example, you can use PHP magic to add just about anything to your localization object. Hence, no need to ever embed JavaScript within a PHP file.

Now you can call your localized JavaScript vari-ables from your my_script.js file. Here’s an ex-ample of an alert:

Page 49: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax48

It’s really as easy as that. You can now localize JavaScript strings and get the coveted site URL (which we’ll need for Ajax later).

Other Localization TechniquesWhile the wp_localize_script function does great work, it has one inherent flaw: each localized string is on a new line. For plugins that require a lot of lo-calized strings, the size of the page source can easily balloon to unacceptable levels.

To remedy this, we can use two additional localiza-tion techniques: one uses JSON, and the other is a cus-tom function.

The JSON TechniqueThe JSON Technique uses WordPress’built-in JSON class in order to parse our localized variables.

We would use the same localize_vars function, but would modify the way we queue our scripts.

First, let’s create a helper function that will instantiate the JSON class and spit out our localized variables to screen.

alert(my_unique_name.SiteUrl); alert(my_unique_name.OtherText);

Page 50: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 3, Localizing Your Scripts 49

The js_localize function takes in a $name (our ob-ject name) and an array of our localized variables ($vars).

The function then instantiates the JSON class and encodes the variables for output.

Here’s how the code would look when queueing up your scripts.

function js_localize($name, $vars) { ?> <script type='text/javascript'> /* <![CDATA[ */ var <?php echo $name; ?> = <?php require_once(ABSPATH . '/wp-includes/class-json.php'); $wp_json = new Services_JSON(); echo stripslashes($wp_json->encodeUnsafe($vars)); ?>; /* ]]> */ </script> <?php }

<?php js_localize('my_unique_name', localize_vars()); wp_enqueue_script('my_script', WP_CONTENT_URL . 'plugins/my_plugin/my_script.js', array('jquery'), '1.0.0'); ?>

Please note that the js_localize function is run

Page 51: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax50

before the script is queued.

While this technique does eliminate the newlines and creates cleaner source code, it does have one major flaw. It doesn’t work for all languages.

For example, the Turkish language causes the above technique to come crashing down.

However, if you don’t plan on having additional languages and want localization purely for the ability to access the JavaScript variables, then I would recommend this technique.

A Custom FunctionFor those wanting to eliminate the newlines caused by wp_localize_scripts, and still have the ability to handle complex languages, then a cus-tom function will have to suffice.

We’ll use the same exact code to queue our scripts, but the js_localize function will change a bit.

My technique is to iterate through the localized variables, save them to an array, and output the array to screen.

Page 52: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 3, Localizing Your Scripts 51

function js_localize($name, $vars) { $data = "var $name = {"; $arr = array(); foreach ($vars as $key => $value) { $arr[count($arr)] = $key . " : '" . esc_js($value) . "'"; } $data .= implode(",",$arr); $data .= "};"; echo "<script type='text/javascript'>\n"; echo "/* <![CDATA[ */\n"; echo $data; echo "\n/* ]]> */\n"; echo "</script>\n"; }

It might not be the most poetic thing you’ve ever seen, but it works pretty well, even for those com-plex languages.

Localization ConclusionWithin this chapter you learned the how and the why of JavaScript localization.

The benefits of localizing your JavaScript are:

No need to embed • JavaScript and PHP.

Can capture • PHP variables without having to load the WordPress environment.

Can enable others to translate your • JavaScript strings.

Page 53: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax52

You also learned three different techniques to achieve localization.

Using • wp_localize_script - Recommended for general use.

Using • JSON - Recommended for non-complex localization and performance.

Using a Custom • Function - Recommended for complex localization and performance.

Page 54: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 4

Properly Formatting jQuery for WordPress Use

Page 55: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax54

Properly Formatting jQuery for WordPress UseDid I mention we would be using the uber-awe-some jQuery library for these examples?

Since we are, it’s a good idea to make sure your jQuery code is formatted appropriately for Word-Press.

Topics covered in this section will be:

Namespacing.•

Public properties/functions.•

Private variables/functions.•

You may have your own preference on how to code your own jQuery scripts, but the examples I give are based on various painful experiences of trial and error (remember my drunk friend?). In my examples, I’m not going for creative program-ming; I’m going for readability.

NamespacingFor those not familiar with namespacing, let me give you a brief example.

Say that there are two plugins, both having the function of readCookie, which is not that un-

Page 56: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 4, Properly Formatting jQuery for WordPress Use 55

common since it is a very usable function from QuirksMode.

The result would be a JavaScript error and a pos-sibly non-functioning script.

Namespacing resolves this issue by enclosing the readCookie function inside of a unique name that can only be called from within the namespace. The result? No conflicts!

So how does one namespace?

Let’s start off with some basic jQuery starter code:

The jQuery(document).ready() code starts initializ-ing before a window has finished loading.

An additional benefit is you can use the above code ad-inifitum, unlike the selfish onLoad event (theme and plugin authors using the onLoad event are just asking to get their butts kicked by other developers).

One thing you might notice is the $ symbol. It’s a symbol that both jQuery and Prototype use. Since

jQuery(document).ready(function() { var $ = jQuery; });

Page 57: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax56

WordPress runs jQuery in no-conflict mode, you must use the jQuery object instead of the $ sym-bol.

However, since I like using the $ symbol for the sake of simplicity, I declare the $ symbol inside the scope of the load event and assign it the jQuery object.

From there, we use the jQuery plugin authoring guidelines to assign our namespace.

Here’s an example:

Never, ever, use the onLoad event to initialize a script. You might just find some nasty “surprises” in your mail.

As you can see from the above code, I created a namespace called my_script_namespace using the $ reference. Creating a namespace is as simple as that.

Public Properties/FunctionsSince we’re not creating a jQuery plugin, we’ll stray slightly from the “guidelines”.

There are certain variables, properties, and func-tions you will want others to be able to publicly call.

jQuery(document).ready(function() { var $ = jQuery; $.my_script_namespace = {}; });

Page 58: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 4, Properly Formatting jQuery for WordPress Use 57

Here’s an example of each type:

The init function is your publicly accessible ini-tialization function call. You execute code direct-ly inside this function without referencing other functions.

The my_function function is probably as close to a property as you’ll get in JavaScript. You can ex-ecute code, and call a private function (which we’ll declare later).

And vars is a variable declared as an empty object, which can be added to from an additional private variable of the same name (not declared yet).

Private Variables/FunctionsPrivate variables and functions are necessary for when you don’t want other scripts to call your functions directly (use a property for indirect ac-cess).

jQuery(document).ready(function() { var $ = jQuery; $. my_script_namespace = { init: function() { /*my init code */}, my_function: function(obj) { /*do some stuff*/ _my_function($(obj));}, vars: {} }; });

Namespacing prevents function naming conflicts.

Page 59: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax58

jQuery(document).ready(function() { var $ = jQuery; $. my_script_namespace = { init: function() { /*my init code */}, my_function: function(obj) { /*do some stuff*/ _my_function($(obj));}, vars: {} }; //End of my_script_namespace var myvars = $.my_script_namespace.vars; //private variable

function _my_function(obj) { /*private function*/ //my private code here } });

A script with private variables and functions looks like this:

Notice that our namespace ended before the dec-laration of the private functions and variables. Also note the commas separating the properties, functions, and variables in the namespace.

All of the functions and variables (that are private) are now within the scope of the jQuery(document).ready() enclosure. Since they exist only within the scope of the enclosure, you will be able to avoid the conflict of having duplicate JavaScript functions.

The beauty of this is that the namespace’d func-tions can call the in-scope functions. Further-more, the private variables can reference the namespace’d variables.

Page 60: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 4, Properly Formatting jQuery for WordPress Use 59

By placing the $.my_script_namespace.init(); to-wards the end, you ensure that the rest of the code has finished loading. From there, you can call

If you’ve been looking closely at the code, you’ll observe that nothing is being done here. After the namespace is added, no function is being called. The namespace is there, but nothing is happen-ing.

It’s as if the namespace doesn’t exist (cue scary music).

The best way I’ve found to load code is to call a public initialization function immediately before the ending of the loading enclosure:

jQuery(document).ready(function() { var $ = jQuery; $. my_script_namespace = { init: function() { /*my init code */}, my_function: function(obj) { /*do some stuff*/ _my_function($(obj));}, vars: {} }; //End of my_script_namespace var myvars = $.my_script_namespace.vars; function _my_function(obj) { //my code here } $.my_script_namespace.init(); });

Page 61: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax60

Conclusion for Properly Formatting jQuery for WordPressIn this section you learned how to properly for-mat jQuery code for WordPress.

The examples given are my personal guidelines from painful experience, but you’re welcome to experiment with your own technique.

the public initialization function (conveniently named init).

Advanced users (such as other plugin authors) may need to call this from outside the original script.

In this case, you’ll want to use the above script as a dependency, and use your own load event to call the script using the regular jQuery namespace.

jQuery(document).ready(function() { jQuery.my_script_namespace.init(); });

Page 62: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 5

Preventing Plugins From Loading

Page 63: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax62

Preventing Plugins From LoadingThere may be cases when using Ajax where you want to prevent WordPress plugins from loading in your Ajax processor (Ajax processors will be cov-ered later).

To call this technique a hack is giving it a big compliment.

If you look at your wp-settings.php file, there is a conditional right before it loads your active plu-gins.

It checks to see if the constant WP_INSTALLING is de-fined. If it is, WordPress doesn’t load any plugins.

So the trick to “trick” WordPress is to define the constant, and then load the WordPress environ-ment.

After that, we can manually load any plugins de-sired.

Here’s some sample code:

Page 64: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 5, Preventing Plugins From Loading 63

The above code demonstrates how to load just one plugin in order to use its functions if necessary.

If you have multiple plugins to load, you can use a foreach statement. This is the code taken directly from wp-settings.php that you can modify to your heart’s content.

<?php header('Content-Type: text/html'); define('WP_INSTALLING', true); //Adjust the dirnames to match the path to your wp-load file. $root = dirname(dirname(dirname(dirname(dirname(__FILE__))))); if (file_exists($root.'/wp-load.php')) { // WP 2.6 require_once($root.'/wp-load.php'); } else { // Before 2.6 require_once($root.'/wp-config.php'); } $plugin = 'your-plugin-directory/your-plugin-file.php'; // Validate plugin filename if ( !validate_file($plugin) && '.php' == substr($plugin, -4) && file_exists(WP_PLUGIN_DIR . '/' . $plugin)) { include_once(WP_PLUGIN_DIR . '/' . $plugin); } unset($plugin); ?>

Page 65: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax64

I would advise that you use this technique on a case-by-case basis.

If writing code for the masses, do not use this tech-nique as the path to wp-load.php is unpredictable. However, for personal use and/or client sites, this technique should be acceptable since you’ll likely know where wp-load.php can be found.

$current_plugins = get_option( 'active_plugins' ); if ( is_array($current_plugins)) { foreach ( $current_plugins as $plugin ) { // $plugin looks like: your-plugin-dir/your-plugin-file.php switch($plugin) { case: 'yourplugin': case: 'anotherplugin': break; default: continue; } if ( !validate_file($plugin) && '.php' == substr($plugin, -4) && file_exists(WP_PLUGIN_DIR . '/' . $plugin)) { include_once(WP_PLUGIN_DIR . '/' . $plugin); } } unset($plugin); } unset($current_plugins);

Page 66: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6

Sending Our First Ajax Request

Page 67: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax66

Sending Our First Ajax Re-questBy now, you should know how to do the follow-ing:

To properly add scripts to WordPress.•

To create well-formed jQuery scripts.•

To localize scripts.•

It’s now time to work on our first Ajax request.

We’ll be doing a simple Ajax call to the WordPress back-end to get the total number of comments on a website. It’s simple, but it’ll demonstrate what’s needed to make that first Ajax request (gotta crawl before you walk).

In this example, I’ll be making use of themes. The later (and more advanced) examples will make use of plugins (so don’t go crying to mama just yet plugin authors).

Here are the files we’ll be working with:

functions.php•

sidebar.php•

my_script.js•

my_script_handler.php (our Ajax processor)•

Page 68: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 67

Set Up the PHP Class to Handle Back-end Op-erationsWe’ll first place our PHP class in our functions.php file. For those not familiar with functions.php, it allows a theme to have access to a handful of cus-tom functions throughout the theme. It’s also a good way to hard-code in plugins.

I’ve created the following class to include in our functions.php file.

my_themewp-content themes functions.php

sidebar.php

my_script.js

my_script_han-dler.php

Base FilesFigure 2.

<?php if (!class_exists('myThemeClass')) { class myThemeClass { /** * PHP 4 Compatible Constructor */ function myThemeClass(){$this->__construct();} /** * PHP 5 Constructor */

Page 69: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax68

function __construct(){ //initialization code here } function get_comments() { //get comment code here } } //End Class myPluginClass } //instantiate the class if (class_exists('myThemeClass')) { $myClassVar = new myThemeClass(); } ?>

The above PHP class is very scaled down, but it sets up a decent structure for future growth.

Since we are going to be retrieving comments us-ing the WordPress back-end, I’ve added in the get_comments method.

Setting up the InterfaceThe next step in sending our Ajax request is to set up some kind of interface that the user will inter-act with on the client-side.

In this particular case, it’s just a link. Yep, a sim-ple link!

We’re going to add this link in our sidebar.php file.

Page 70: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 69

We now have two variables to work with when we deal with our Ajax request: action, and _wpnonce. Please keep in mind the _wpnonce value was gener-

First I’m going to assign a variable our href por-tion of the link.

Use functions.php for theme-wide custom functions.

There are a few things going on here. The wp_nonce_url function is necessary for securing links. It prevents you from following a malicious script and performing an unintended action on the back-end.

Since we’re not accessing anything serious, the wp_nonce_url is overkill, but learning it now will help you later if you want to secure your Ajax re-quests.

In this case, we pass wp_nonce_url our URL and a unique identifier that combines the name of our theme to the action it wants to take.

Now the $link_url has a value similar to this:

<?php $link_url = wp_nonce_url( get_bloginfo('url') . "/?my_page=ajax-processor&action=getcomment", "my-theme_getcomment"); ?>

http://www.yourdomain.com/?my_page=ajax-processor& action=getcomments&_wpnonce=1d8a910922

Page 71: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax70

ated based on your unique string, which in this case is my-theme_getcomments.

Also of note is our action=getcomment variable. Since it’s likely that our Ajax processor will be taking various inputs, we specify what action we want it to take.

Our final link code in the sidebar.php file would look like this:

<?php $link_url = wp_nonce_url( get_bloginfo('url') . "/?my_page=ajax-processor", "my-theme_getcomment"); ?> <a href='<?php echo $link_url; ?>' id='get-comments'><?php _e('Get Comments','get-comments');?></a>

I gave the link an id of get-comments in order to cap-ture it later for events inside of jQuery.

Notice that I used another localization function, _e(). This echoes out the string rather than returning it, as opposed to the __() function. We’ll add in our local-ization code in a later example.

When viewing your theme, you should now see the Get Comments link.

Now it’s time to set up our JavaScript file.

Page 72: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 71

Now we have a good foundation going. As soon as the document has loaded, our initialization function will run. As of right now, the script hasn’t been added to your theme yet, but we’ll get to that soon.

The next step is adding an event to capture when the user clicks on our Get Comments link.

The event code will be placed inside our init func-tion.

Setting Up the JavaScript FileWe’ll now be working with our my_script.js file, which should be empty at this point.

First, let’s add in our loading code foundation:

Now, let’s create a namespace of get_my_comments with a public init function. We’ll also be adding a call to the init function as well.

jQuery(document).ready(function() { var $ = jQuery; });

jQuery(document).ready(function() { var $ = jQuery; $.get_my_comments = { init: function() { /*my init code */} }; //End of get_my_comments namespace $.get_my_comments.init(); });

Page 73: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax72

We have now captured the click event for our link, but nothing is happening yet.

Let’s begin setting up our Ajax object.

jQuery(document).ready(function() { var $ = jQuery; $.get_my_comments = { init: function() { $("a#get-comments").bind("click", function() { return false; } } }; //End of get_my_comments namespace $.get_my_comments.init(); }();

Get Comments LinkFigure 3.

jQuery(document).ready(function() { var $ = jQuery; $. get_my_comments = { init: function() { $("a#get-comments").bind("click", function() { _do_ajax(this); //Call to our private function

Page 74: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 73

return false; } } }; //End of get_my_comments namespace function _do_ajax(obj) { var element = $(obj); //our link object } //end _do_ajax $.get_my_comments.init(); }();

In our init function, we call the private function _do_ajax and pass it our link object.

We then create the _do_ajax function, and create the variable element to hold our object. We place the $() around the obj variable in order to use jQuery actions with it.

The next step is parsing our link object.

function _do_ajax(obj) { var element = $(obj); //our link object var url = wpAjax.unserialize(element.attr('href')); }

The wpAjax object comes from one of our depen-dencies that we haven’t defined yet (wp-ajax-re-sponse).

It takes the href attribute of the passed element and creates an object called url with it.

Page 75: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax74

The url object now has two variables we can access via JavaScript (url._wpnonce and url.action). We could also use the href attribute to to capture the path to our Ajax processor, but I want to show you how to capture this path via localized JavaScript since not all Ajax requests involve links.

Setting up the Ajax ObjectWe’ll be building on the existing _do_ajax func-tion to set up our Ajax object.

I’ll first create an object with the name of s to hold our Ajax data. Why s? Because it’s short and sweet (like my alliteration?).

With our s object, we’ll define our parameters for use with jQuery’s Ajax options.

function _do_ajax(obj) { var element = $(obj); //our link object var url = wpAjax.unserialize(element.attr('href')); var s = {}; s.element = element.attr("id"); s.response = 'ajax-response'; s.type = "POST"; s.url = mythemegetcomments.site_url + "/?my_page=ajax-processor"; s.data = $.extend(s.data, { action: url.action, _ajax_nonce: url._wpnonce }); s.global = false; s.timeout = 30000; } //end _do_ajax

Page 76: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 75

There’s a lot going on here. First, I define the s object. I then assign the variable element our link object, which I wrap with $() in order to access jQuery’s functionality.

I assign the s.element the ID attribute of the link, and set the s.response.

I also set the Ajax request to be POST, give it a URL to the Ajax processor, and assign data that will be passed to our Ajax processor.

With the s.data object, we’ll have access to s.data.action, and s.data._ajax_nonce. We won’t need to use the s.data object, but the action and _ajax_nonce variables will be passed to my_script_handler.php as POST variables.

We have two objects that are undefined right now: wpAjax and mythemegetcomments. So let’s move back to our functions.php file and add them in.

Finalizing the functions.php ClassEarlier in this chapter, I gave you the foundation for a PHP class with a name of myThemeClass.

Here’s what we need to finalize this class:

Finish the • get_comments method.

Page 77: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax76

What we do is call the wp_count_comments function and return its content.

The wp_count_comments function returns an ob-ject with variables approved, moderated, spam, and trash.

Add in our JavaScript FilesIn the constructor, we add the wp_print_scripts ac-tion to point to a method conveniently named add_scripts.

Add in our JavaScript files.•

Add in JavaScript localization.•

Add in String localization.•

The get_comments MethodLet’s concentrate on our get_comments method first.

function get_comments() { return wp_count_comments(); /*wp_count_comments returns an object with values of trash, spam, approved, and moderated*/ }

function __construct(){ add_action('wp_print_scripts', array(&$this,'add_scripts')); }

Page 78: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 77

You might remember from our my_scripts.js file that we had an undefined object of wpAjax. The dependency wp-ajax-response is the script that ini-tializes that object.

If you were to view the HTML source where wp-ajax-response loaded, it would look like this:

Please note that wp_print_scripts will only load scripts on the front-end of the website (not the admin panel).

Now we can concentrate on our add_scripts meth-od.

function add_scripts() { wp_enqueue_script('my_script', get_bloginfo('template_directory').'/my_script.js', array("jquery", "wp-ajax-response") , '2.3'); }

<script type='text/javascript'> /* <![CDATA[ */ var wpAjax = { noPerm: "You do not have permission to do that.", broken: "An unidentified error has occurred." }; try{convertEntities(wpAjax);}catch(e){}; /* ]]> */ </script> <script type='text/javascript' src='http://www.yourdomain.com/wp-includes/js/wp-ajax-response.js?ver=20091119'></script>

Page 79: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax78

The next thing going on is we queue our script (giving it a handle of my_script) and give it the dependencies of jquery and wp-ajax-response.

When viewing the HTML source, your script addi-tion will look similar to this:

Just by looking at the source, you’ll know our script is being queued correctly. Both the jQuery and wp-ajax-response scripts load before our script.

Now we’re almost finished with our add_scripts method. We need to add in some localization.

Add in JavaScript Localization

<script type='text/javascript' src='http://www.yourdomain.com/wp-includes/js/jquery/jquery.js?ver=1.3.2'></script> <script type='text/javascript'> /* <![CDATA[ */ var wpAjax = { noPerm: "You do not have permission to do that.", broken: "An unidentified error has occurred." }; try{convertEntities(wpAjax);}catch(e){}; /* ]]> */ </script> <script type='text/javascript' src='http://www.yourdomain.com/wp-includes/js/wp-ajax-response.js?ver=20091119'></script><script type='text/javascript' src='http://www.yourdomain.com/wp-content/themes/my_theme/my_script.js?ver=2.3'></script>

Page 80: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 79

We’ll be calling several strings that need to be localized in our my_scripts.js file. Here are the strings that need to be localized:

You have•

total•

approved•

comments•

in moderation•

trashed•

spam•

What we’ll do is define a method called get_js_vars and have the method return an array of the localized strings.

function get_js_vars() { return array( 'site_url' => get_bloginfo('url'), 'you_have' => __('You have', 'get-comments'), 'total' => __('total', 'get-comments'), 'approved' => __('approved', 'get-comments'), 'comments' => __('comments', 'get-comments'), 'in_moderation' => __('in moderation', 'get-comments'), 'trashed' => __('trashed', 'get-comments'), 'spam' => __('spam', 'get-comments') ); } //end get_js_vars

Page 81: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax80

We assign the wp_localize_script a handle of my_script (the same handle as our my_script.js file), give it an object name of mythemegetcomments, and pass it array of strings to be localized (via the get_js_vars method).

When viewing the HTML source, you should now see something similar to this example:

Now let’s return to our add_scripts method and finalize our localization there.

function add_scripts() { wp_enqueue_script('my_script', get_bloginfo('template_directory').'/my_script.js', array("jquery", "wp-ajax-response") , '2.3'); wp_localize_script( 'my_script', 'mythemegetcomments', $this->get_js_vars()); } //End add_scripts

<script type='text/javascript' src='http://www.yourdomain.com/wp-includes/js/jquery/jquery.js?ver=1.3.2'></script> <script type='text/javascript'> /* <![CDATA[ */ var wpAjax = { noPerm: "You do not have permission to do that.", broken: "An unidentified error has occurred." }; try{convertEntities(wpAjax);}catch(e){}; /* ]]> */ </script> <script type='text/javascript' src='http://www.yourdomain.com/wp-includes/js/wp-ajax-response.js?ver=20091119'></script> <script type='text/javascript'>

Page 82: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 81

/* <![CDATA[ */ var mythemegetcomments = { site_url: "http://www.yourdomain.com/?my_page=ajax-processor", you_have: "You have", total: "total", approved: "approved", comments: "comments", in_moderation: "in moderation", trashed: "trashed", spam: "spam" }; /* ]]> */ </script> <script type='text/javascript' src='http://www.yourdomain.com/wp-content/themes/defaultcopy/my_script.js?ver=2.3'></script>

You now have access to the mythemegetcomments ob-ject in your my_script.js file.

Add in String LocalizationWe’re almost done with the localization. We just need to add support for localization into our class.

Let’s go back to our constructor and add in an init action and point it to a method named (what else?) init.

function __construct(){ add_action('wp_print_scripts', array(&$this,'add_scripts')); add_action('init', array(&$this, 'init')); }

Page 83: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax82

We next create the init method and initialize the localization.

Add in Query Variable SupportFinally, we need to add in query variable support for our Ajax processor. We’ll start by adding the load_page and query_trigger methods.

function load_page() { $pagepath = TEMPLATEPATH . '/'; switch(get_query_var('my_page')) { case 'ajax-processor': include($pagepath . 'my_script_handler.php'); exit; default: break; } } //end function load_page function query_trigger($queries) { array_push($queries, 'my_page'); return $queries; } //end function query_trigger

function init() { load_theme_textdomain('get-comments'); }

The query_trigger method adds in our query vari-able and the load_page handles the loading of the Ajax processor.

Up next is adding in the action for load_page and the filter for query_trigger. We’ll be adding the

Page 84: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 83

//instantiate the class if (class_exists('myThemeClass')) { $myClassVar = new myThemeClass(); add_action('template_redirect', array(&$myClassVar,'load_page')); add_filter('query_vars', array(&$myClassVar,'query_trigger')); }

action and filter right after the class is instanti-ated.

<?php if (!class_exists('myThemeClass')) { class myThemeClass { /** * PHP 4 Compatible Constructor */ function myThemeClass(){$this->__construct();} /** * PHP 5 Constructor */ function __construct(){ add_action('wp_print_scripts', array(&$this,'add_scripts')); add_action('init', array(&$this, 'init')); } function init() { load_theme_textdomain('get-comments'); } function get_comments() { return wp_count_comments(); /*wp_count_comments returns an object with values of trash, spam, approved, and moderated*/ } function add_scripts() { wp_enqueue_script('my_script', get_

The Finalized functions.php Class

Page 85: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax84

bloginfo('template_directory').'/my_script.js', array("jquery", "wp-ajax-response") , "2.3"); wp_localize_script( 'my_script', 'mythemegetcomments', $this->get_js_vars()); } //End add_scripts function get_js_vars() { return array( 'site_url' => get_bloginfo('url'), 'you_have' => __('You have', 'get-comments'), 'approved' => __('approved', 'get-comments'), 'comments' => __('comments', 'get-comments'), 'in_moderation' => __('in moderation', 'get-comments'), 'trashed' => __('trashed', 'get-comments'), 'spam' => __('spam', 'get-comments') ); } //end get_js_vars function load_page() { $pagepath = TEMPLATEPATH . '/'; switch(get_query_var('my_page')) { case 'ajax-processor': include($pagepath . 'my_script_handler.php'); exit; default: break; } } //end function load_page function query_trigger($queries) { array_push($queries, 'my_page'); return $queries; } //end function query_trigger } //End Class myPluginClass } //instantiate the class if (class_exists('myThemeClass')) {

Page 86: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 85

We’re now done with our class. Phew! Let’s move on to finalizing our first Ajax request.

Finalizing the Ajax RequestLet’s move back to our my_script.js file.

The last thing we did with it is add in some data for our s object.

Let’s add in a success and error function for our Ajax request.

function _do_ajax(obj) { var element = $(obj); //our link object var url = wpAjax.unserialize(element.attr('href')); var s = {}; s.element = element.attr("id"); s.response = 'ajax-response'; s.type = "POST"; s.url = mythemegetcomments.theme_url + "/my_script_handler.php"; s.data = $.extend(s.data, { action: url.action, _ajax_nonce: url._wpnonce }); s.global = false; s.timeout = 30000; s.success = function(r) { alert("Success!"); }

$myClassVar = new myThemeClass(); add_action('template_redirect', array(&$myClassVar,'load_page')); add_filter('query_vars', array(&$myClassVar,'query_trigger')); } ?>

Page 87: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax86

jQuery(document).ready(function() { var $ = jQuery; $.get_my_comments = { init: function() { $("a#get-comments").bind("click", function() { _do_ajax(this); //Call to our private function return false; }); } }; //End of get_my_comments namespace function _do_ajax(obj) { var element = $(obj); //our link object var url = wpAjax.unserialize(element.attr('href'));

As seen in the above code, our success and error functions are each defined to alert us to which one occurs.

Now it’s time to add in our Ajax call, which is the last step in sending our first Ajax request.

$.ajax(s);

s.error = function(r) { alert("Epic Fail!"); } } //end _do_ajax

Sending our Ajax request is that simple!

Now here’s a look at our finalized code for the first Ajax request (we’ll fill in the success and error functions later):

Page 88: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 6, Sending Our First Ajax Request 87

So what now?

Well, head to your website and click on the Get Comments link.

And what are you met with? EPIC FAILURE!

var s = {}; s.element = element.attr("id"); s.response = 'ajax-response'; s.type = "POST"; s.url = mythemegetcomments.site_url + "/?my_page=ajax-processor"; s.data = $.extend(s.data, { action: url.action, _ajax_nonce: url._wpnonce }); s.global = false; s.timeout = 30000; s.success = function(r) { alert("Success!"); } s.error = function(r) { alert("Epic Fail!"); }

$.ajax(s); } //end _do_ajax $.get_my_comments.init(); });

Page 89: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax88

I can hear you now, “You mean we just did all that work just to end up in failure?”

Yep, it’s like watching one of those good movies that have a horrible ending (Matrix trilogy any-one?).

Before you grab your stick and beat down the au-thor, be reminded that we have yet to create our my_script_hander.php file. It doesn’t exist, so we get a 404 error and the Ajax request ends up in an error.

So in a way, we were successful. We successfully sent our first request, but hit a brick wall.

So what’s next? Why, I’m glad you asked.

It’s now time to create our my_script_handler.php file and add in some security.

Epic Fail!Figure 4.

Page 90: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 7

Server-Side Security for Ajax

Page 91: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax90

In the above example, there are a few things going on.

We first specify a header with a content type and charset. Since JavaScript sends requests using UTF-8, it’s important to set our header to the same charset.

Server-Side Security for AjaxLet’s go ahead and create our empty my_script_handler.php file.

Once you have done that, go ahead and click on the Get Comments link.

Success! Well, sort of. Nothing is happening, but at least we’re talking to our Ajax processor.

Loading the WordPress EnvironmentTo get our Ajax processor ready for business, we need to add a few things.

<?php header('Content-Type: text/html; charset=UTF-8'); define('DOING_AJAX', true);

Page 92: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 7, Server-Side Security for Ajax 91

The second thing done here is we define a constant called DOING_AJAX and assign it a value of true. The constant DOING_AJAX is used to avoid cron jobs, and to allow extra time for cookie validation.

Now we’re set to move onto the next step: check-ing our passed nonce.

Performing a Nonce CheckAs a reminder, we passed two POST variables to the my_script_handler.php file:

action• - The action we are taking.

_ajax_nonce• - The nonce we will be verifying against.

Another reminder is that we used the string my-theme_getcomment to create our nonce back in the sidebar.php file.

Thankfully, WordPress provides an easy function to verify passed Ajax nonces. The function, you ask? It’s called check_ajax_referer.

If we were to send an Ajax request without a nonce and included the function check_ajax_referer in our script handler, the Ajax processor would send back a string with value “-1”.

Page 93: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax92

Now what happens when we go back to our web-site and click on that Get Comments link?

If our nonce checking is working, you should re-ceive a blank alert box. If the nonce failed, you should receive a “-1”.

You can test this out by changing the string in the check_ajax_referer function call. Click again on the Get Comments link and observe that the alert box now shows a “-1”.

Let’s now go back to our my_script.js file and update the success function to alert us to the re-sponse that my_script_handler.php file sends back.

Effectively, when check_ajax_referer fails, the Ajax server-side processing is stopped cold.

Adding in the check_ajax_referer function in our Ajax processor will look like this:

//Check the AJAX nonce check_ajax_referer('my-theme_getcomment');

s.success = function(r) { alert(r); }

Page 94: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 7, Server-Side Security for Ajax 93

We should be good to go as far as performing some basic security checks in our Ajax processor, so let’s move on to some server-side processing fun.

Pssst... Don’t forget to change the string in check_ajax_referer back to my-theme_getcomment.

Page 95: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner
Page 96: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 8

Server-Side Processing of Ajax Requests

Page 97: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax96

Server-Side Processing of Ajax RequestsPreviously, we added in a security check for our passed nonce. Now it’s time to do some process-ing of the passed data.

We’ll be interacting with our class defined in the functions.php file.

Now you may be wondering, “Why don’t we just include the code needed in myPluginClass inside the Ajax processor?”

There’s absolutely no problem with that, but I want to show you how you can easily interact with classes from within your Ajax processor.

Let’s move on. Next up is capturing our action variable.

Since the action variable was passed as a POST vari-able, we check to see if it’s set. If it’s set, we assign it to the $action variable.

if (isset($_POST['action'])) { $action = $_POST['action']; } die(''); //Place at the end of the file

Page 98: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 8, Server-Side Processing of Ajax Requests 97

The die function is placed at the end of the file for a few reasons:

Some servers add in code at the end of files. •The die function prevents this.

There’s no logical need to let the file go on any •longer.

Within the conditional, we add in a switch state-ment to determine what action is being taken.

A switch statement for only one value is extreme overkill, but it demonstrates how you can check for various actions and perform different tasks.

Now that we’ve added in our switch statement, it’s time to interact with our myPluginClass class.

if (isset($_POST['action'])) { $action = $_POST['action']; switch($action) { case 'getcomment': break; case 'otheraction': break; default: break; } //end switch } //end if

Page 99: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax98

Since the Ajax processor is technically being in-cluded within the load_page method, the Ajax pro-cessor code is in scope of the myPluginClass instan-tiation.

As a result, we can use the $this variable to access the methods within myPluginClass.

We’ll be calling $this->get_comments() to retrieve all of our comment counts.

The above code assigns the variable $comment_count with the result from the get_comments method.

Remember, the get_comments method returns an object with four variables:

approved•

moderated•

spam•

trash•

We’ll be dealing with these variables in the next section when we work on sending our Ajax re-sponse.

case 'getcomment': $comment_count = $this->get_comments(); break;

Adding a ‘die’ function at the end of your Ajax processor prevents your host from appending unnecessary code.

Page 100: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 9

Server-Side Responses of Ajax Requests

Page 101: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax100

Server-Side Responses of Ajax RequestsA super easy way of sending back a request to our my_script.js is just to echo out the variables.

But, we have four keys we have to deal with, and parsing that in JavaScript will be a pain.

So what’s one to do?

Fortunately, WordPress has the WP_Ajax_Response class.

Let’s go ahead and instantiate the class and assign it to the $response variable.

Now it’s time to add some data to our $response variable by calling the add method.

When we call the add method, we pass it an as-sociative array with various parameters. Here are the parameters we’ll be dealing with:

case 'getcomment': $comment_count = $myClassVar->get_comments(); $response = new WP_Ajax_Response(); break;

Page 102: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 9, Server-Side Responses of Ajax Requests 101

what• - A string that is the XMLRPC response type. In our case, we’ll just call it “getcomments”.

supplemental• - An associative array of strings. We’ll be passing our $comment_count keys here.

When calling our add method, we’ll have access to all of the various parameters with JavaScript, so it’s best to add them in appropriately.

There is an additional parameter for the add meth-od we could be using called data. This parameter is used when sending just a string of data.

But we have four pieces of data we need to send back, so that means if we used the data parameter, we’d have to create four separate responses by call-ing the add method each time.

The supplemental parameter allows us to return multiple values with just one response. We’ll find out later just how easy it is to get these values via JavaScript.

Alright, enough talking. Let’s get back to the code and call the add method already.

Page 103: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax102

As you can see from the above code, we pass to the add method an array with parameters what and supplemental.

The supplemental parameter itself takes another array, which we assign our values from $comment_count.

We’re almost done. All that’s left is to send the response.

case 'getcomment': $comment_count = $this->get_comments(); $response = new WP_Ajax_Response(); $response->add(array( 'what' => 'getcomments', 'supplemental' => array( 'awaiting_moderation' => $comment_count->moderated, 'approved' => $comment_count->approved, 'spam' => $comment_count->spam, 'trashed' => $comment_count->trash ) ));

It really is as simple as that. Here’s the full code for the Ajax processor:

$response->send();

Page 104: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 9, Server-Side Responses of Ajax Requests 103

<?php header('Content-Type: text/html; charset=UTF-8'); define('DOING_AJAX', true); //Check the AJAX nonce check_ajax_referer('my-theme_getcomment'); if (isset($_POST['action'])) { $action = $_POST['action']; switch($action) { case 'getcomment': $comment_count = $this->get_comments(); $response = new WP_Ajax_Response(); $response->add(array( 'what' => 'getcomments', 'supplemental' => array( 'awaiting_moderation' => $comment_count->moderated, 'approved' => $comment_count->approved, 'spam' => $comment_count->spam, 'trashed' => $comment_count->trash ) )); $response->send(); break; case 'otheraction': break; default: break; } //end switch } //end if die(''); ?>

Page 105: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax104

If you click on your Get Comments link, you’re alert-ed that you have an [object XMLDocument].

It’s time to go back to our my_scripts.js file and do some client-side processing on this returned object.

Page 106: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 10

Client-Side Processing/Parsing

Page 107: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax106

Client-Side Processing/ParsingIn this section we’ll be working with our my_script.js file to do some client-side processing/parsing.

We’ll be completing the s.success function, which currently looks like this:

Nothing spectacular is going on here other than a simple alert box that doesn’t show us anything useful.

Right now as it stands, the r variable is an XML Document object, which we can do nothing with at the moment.

Let’s change that.

Parsing the XML Docu-ment ObjectDo you remember our JavaScript dependency of wp-ajax-response? We’ll be using that same script to parse the passed XML Document.

s.success = function(r) { alert(r); }

Page 108: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 10, Client-Side Processing/Parsing 107

The sub function from the wpAjax object that is used for parsing is called parseAjaxResponse, which takes three arguments:

Our passed XML Document.•

Our response type (in our case, • ajax-re-

sponse, which was defined in the s object as s.response).

Our element (which we defined in • s.element).

The call to wpAjax.parseAjaxResponse would look like this:

The keyword this is used instead of the object s since the s object is out of scope. However, all of the values captured in the s object are now avail-able with the this keyword.

Our parsed response is now stored inside the res variable.

Getting to that data is another matter. Let’s move on to processing the parsed data.

s.success = function(r) { res = wpAjax.parseAjaxResponse(r,this.response,this.element); }

Page 109: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax108

Processing the DataWe could process the data using the easy (but not scalable way) by assigning another variable the object we are after.

This would be fine, but what if you had multiple responses? In our particular case, we don’t, but I’m still going to show you how to handle mul-tiple responses for scalability purposes.

The way to handle multiple responses is by using the jQuery.each function.

s.success = function(r) { var res = wpAjax.parseAjaxResponse(r,this.response,this.element); var response = res.responses[0]; }

The $.each function acts as an interator through an array. It’ll go through each of the responses

s.success = function(r) { var res = wpAjax.parseAjaxResponse(r,this.response,this.element); $.each( res.responses, function() { //Do stuff here }); }

Page 110: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 10, Client-Side Processing/Parsing 109

and allow us to take action on each one.

Within the $.each function, we’ll be using the this object to access each response.

The this object should contain the what and sup-plemental data we sent back using the my_script_handler.php file.

As a reminder, here’s what the supplemental data should contain:

moderated•

approved•

spam•

trash•

Here’s what we should expect the this object to contain:

supplemental

what

moderated

approved

spam

trash

this

The “this” objectFigure 5.

Page 111: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax110

Let’s now concentrate on the this.what value, which should be getcomments.

A simple switch statement in the $.each function will aid us in picking out the what argument.

Again, a switch statement for one value is extreme overkill, but it’ll show you how to handle mul-tiple responses.

The switch statement checks the what variable for several values. In our case, we check for a case of “getcomments”.

s.success = function(r) { var res = wpAjax.parseAjaxResponse(r,this.response,this.element); $.each( res.responses, function() { switch(this.what) { case "getcomments": //Do stuff here break; case "something else": break; default: break; } }); //end each } //end success

Page 112: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 10, Client-Side Processing/Parsing 111

It’s within the “getcomments” case that we’ll ac-cess our supplemental data.

Getting the supplemental data is super easy thanks to the parseAjaxResponse function.

Accessing the supplemental data is as simple as call-ing the key values we assigned in my_script_han-dler.php:

this.supplemental.moderated•

this.supplemental.approved•

this.supplemental.spam•

this.supplemental.trash•

Let’s go ahead and assign the supplemental data to four JavaScript variables:

moderation_count•

approved_count•

spam_count•

trashed_count•

Our code would look like this:

Page 113: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax112

Our four variables are now assigned the values re-trieved from the Ajax request.

We’re done with the client-side processing/pars-ing.

Let’s move on to the output.

s.success = function(r) { var res = wpAjax.parseAjaxResponse(r,this.response,this.element); $.each( res.responses, function() { switch(this.what) { case "getcomments": var moderation_count = this.supplemental.moderated; var approved_count = this.supplemental.approved; var spam_count = this.supplemental.spam; var trashed_count = this.supplemental.trash; break; case "something else": break; default: break; } //end switch });//end each } //End success

Page 114: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 11

The Output

Page 115: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax114

The OutputThis is the grand finale of sending our Ajax re-quest.

Here’s a brief summary of what we’ve accom-plished so far:

We sent our first Ajax request and ended up in •EPIC FAILURE (not my fault, I swear).

We secured our Ajax request with the use of •nonces.

We processed the Ajax request and prepared •for a response.

We sent the response back to our JavaScript •file.

We parsed the response and processed it for •later use.

So what’s left to do? Output the data, of course.

As a reminder, we have four JavaScript variables we’ll be using for the output:

moderation_count•

approved_count•

spam_count•

trashed_count•

Page 116: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 11, The Output 115

It’s slightly involved here, but don’t be intimidat-ed. All we’re doing here is building our string with a combination of the strings stored in the

And do you remember the localized JavaScript variables we defined way back when? Ah, you don’t? Well, rather than force you to thumb your way back 20 or so pages, here they are again (I know, you’re welcome).

So let’s get to it.

Let’s first create our moderation string:

<script type='text/javascript'> <script type='text/javascript'> /* <![CDATA[ */ var mythemegetcomments = { site_url: "http://www.yourdomain.com", you_have: "You have", approved: "approved", comments: "comments", in_moderation: "in moderation", trashed: "trashed", spam: "spam" }; /* ]]> */ </script>

//Strings var moderation = mythemegetcomments.you_have + " " + moderation_count + " " + mythemegetcomments.comments + " " + mythemegetcomments.in_moderation + ".";

Page 117: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax116

Another small problem: what if we have a lot of spam and approved comments? Would you like to have an output such as, “You have a total of 1023930 approved comments”?

mythemegetcomments object and the JavaScript vari-ables we previously defined.

The moderation string should contain something similar to:

Since we have that down, let’s go ahead and create the rest of the strings.

"You have x comments in moderation."

//Strings var moderation = mythemegetcomments.you_have + " " + moderation_count + " " + mythemegetcomments.comments + " " + mythemegetcomments.in_moderation + "."; var approved = mythemegetcomments.you_have + " " + approved_count + " " + mythemegetcomments.approved + " " + mythemegetcomments.comments + "."; var spam = mythemegetcomments.you_have + " " + spam_count + " " + mythemegetcomments.spam + " " + mythemegetcomments.comments + "."; var trashed = mythemegetcomments.you_have + " " + trashed_count + " " + mythemegetcomments.trashed + " " + mythemegetcomments.comments + ".";

Page 118: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 11, The Output 117

Um, no, right? Since adding in the comma sepa-ration for groups of thousands is tedious using JavaScript, let’s get PHP to do it (ah, the conve-nience of Ajax).

Let’s venture back to our my_script_handler.php file and make use of the number_format function.

The number_format function will be wrapped around our $comment_count keys to transform a count such as 4209094 into 4,209,094.

case 'getcomment': $comment_count = $this->get_comments(); $response = new WP_Ajax_Response(); $response->add(array( 'what' => 'getcomments', 'supplemental' => array( 'awaiting_moderation' => number_format($comment_count->moderated), 'approved' => number_format($comment_count->approved), 'spam' => number_format($comment_count->spam), 'trashed' => number_format($comment_count->trash) ) )); $response->send();

We now have our strings finalized.

There’s two ways to go about an output:

Page 119: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax118

Since we’ll be outputting to the div, let’s add in some line breaks to our JavaScript strings using the br tag.

Use • alert boxes.

Output to the theme.•

Alert boxes are messy and should be used rarely, if ever. I personally only use them for debugging purposes.

Outputting to the theme is the correct way to go. But first we must find a box (err, div) on the theme to output to.

Let’s go back into our sidebar.php file and create an empty div with an id of “get-comments-out-put”. We’ll be placing this div below the Get Com-ments link.

The code will look like this:

<?php $link_url = wp_nonce_url( get_bloginfo('url') . "/?my_page=ajax-processor&action=getcomment", "my-theme_getcomment"); ?> <a href='<?php echo $link_url; ?>' id='get-comments'><?php _e('Get Comments','get-comments');?></a> <div id="get-comments-output"></div>

Page 120: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 11, The Output 119

//Strings var moderation = mythemegetcomments.you_have + " " + moderation_count + " " + mythemegetcomments.comments + " " + mythemegetcomments.in_moderation + ".<br />"; var approved = mythemegetcomments.you_have + " " + approved_count + " " + mythemegetcomments.approved + " " + mythemegetcomments.comments + ".<br />"; var spam = mythemegetcomments.you_have + " " + spam_count + " " + mythemegetcomments.spam + " " + mythemegetcomments.comments + ".<br />"; var trashed = mythemegetcomments.you_have + " " + trashed_count + " " + mythemegetcomments.trashed + " " + mythemegetcomments.comments + ".";

Groovy. Now we’re ready to output. And it’s go-ing to be a lot easier than you think.

$("div#get-comments-output").html(moderation + approved + spam + trashed);

Ah, I love it when things are simple, and that above code snippet is as easy as it gets.

We simply capture the div with the id “get-com-ments-output”. We then alter its inner-HTML and fill it with our strings.

Page 121: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax120

Now when you click on the Get Comments link, you should see something similar to this:

s.success = function(r) { var res = wpAjax.parseAjaxResponse(r,this.response,this.element); $.each( res.responses, function() { switch(this.what) { case "getcomments": var moderation_count = this.supplemental.awaiting_moderation; var approved_count = this.supplemental.approved; var spam_count = this.supplemental.spam; var trashed_count = this.supplemental.trashed; //Strings var moderation = mythemegetcomments.you_have + " " + moderation_count + " " + mythemegetcomments.comments + " " + mythemegetcomments.in_moderation + ".<br />";

The Final OutputFigure 6.

I’ll leave you with the final s.success function code.

Page 122: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 11, The Output 121

var approved = mythemegetcomments.you_have + " " + approved_count + " " + mythemegetcomments.approved + " " + mythemegetcomments.comments + ".<br />"; var spam = mythemegetcomments.you_have + " " + spam_count + " " + mythemegetcomments.spam + " " + mythemegetcomments.comments + ".<br />"; var trashed = mythemegetcomments.you_have + " " + trashed_count + " " + mythemegetcomments.trashed + " " + mythemegetcomments.comments + "."; $("div#get-comments-output").html(moderation + approved + spam + trashed); break; case "something else": break; default: break; } //end switch });//end each } //End success

We’re done! The output is finalized.

Now if you’re masochistic (grabbing my whip), keep reading on for some real-world examples of Ajax and WordPress in use.

Keep in mind I’ll expect you to remember the fol-lowing:

How to add script dependencies.•

How to modify your • functions.php file.

Alerts are good for debugging purposes. They should be used in production rarely, if ever.

Page 123: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax122

How to develop a plugin class.•

How to create your Ajax object for sending.•

How to secure your Ajax request using nonc-•es.

How to send back an Ajax response.•

How to parse/process the response.•

And also remember: no matter what you do, the Internet troll is out there watching (Jay and Silent Bob are also on call).

Page 124: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12

Example #1: WP Grins Lite

Page 125: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax124

Example #1: WP Grins LiteThe WordPress plugin WP Grins is very simple in what it accomplishes: it provides clickable smilies that one can insert into a post or comment.

WP Grins is a great way to spice up a rather dull looking comments form. The biggest drawback WP Grins has for me, however, is that it uses the heavy-weight Prototype JavaScript framework.

None of my other installed plugins used Proto-type, and neither did my theme. Prototype (the version included with WordPress) weighs in at 127

WP Grins Screenshot - Comment AreaFigure 7.

Page 126: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 125

KBs per page load. For just adding simple “grins” to a page, this was unacceptable overhead.

As I explored into the code, I found it rather trivial to port it over to the much lighter-weight jQuery library. Thus, the name was born for the new pl-ugin: WP Grins Lite.

As I explored more into the code, I found several more issues (no disrespect to Alex King here):

The JavaScript was embedded within the PHP •code. Not a huge issue, but it’s a lot cleaner when separated. In fact, my first version of WP Grins Lite still used the embedded JavaScript.

There was no class structure to prevent func-•tion name conflicts. Granted, the function names were prefixed with “wp_grins”, so no big deal here.

The smilies showed up in weird places. Lim-•ited page detection was used, but more was needed.

No option to display smilies manually in the •post comments section. This wasn’t a big deal to me, but it was to one of the users. Adding this option in would be a moderate challenge.

Page 127: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax126

Adding an option to manually insert the smilies wasn’t exactly trivial. After some brainstorming, I determined what was needed:

A separation of PHP and JavaScript. With the •original WP Grins, the two were interspersed, and there was no way to call the grins manually without them also showing up via JavaScript.

An admin panel for the manual option. I •could have gone with having the user manu-ally fill in a config option in the raw file, but a good plugin author never makes his users delve into the code. It’s like trying to pick up single women at a marriage seminar. Not a good idea.

Have a dedicated function for printing out the •grins. This would aid manual showing and JavaScript use.

For JavaScript use, have the smilies returned •via Ajax. I thought about this one for a while, and finally determined Ajax was best used for this one. I could have printed the smilies as a localized variable, but the inner-programmer in me started screaming at this possibility.

So now you have my justifications for the changes I made to the plugin.

Forcing users to manually edit code is like trying to pick up single women at a marriage seminar. Not a good idea.

Page 128: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 127

In order to begin this journey, let’s take a look at WP Grin Lite’s file structure.

Let’s begin with the file wp-grins.php.

The WPGrins Class

js

wp-grins.php

ajax-processor.php

admin-panel.php

wp-grins-lite

wp-grins.js

WP Grins Lite File StructureFigure 8.

if (!class_exists('WPGrins')) { class WPGrins { var $adminOptionsName = "wpgrinslite"; /** * PHP 4 Compatible Constructor */ function WPGrins(){$this->__construct();} /** * PHP 5 Constructor */ function __construct(){ } } }

Page 129: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax128

Shown above is our basic skeleton class called WPGrins.

There’s nothing in it as of this point, so let’s add in some methods so we can get some smily goodness.

The ConstructorFirst up is the constructor.

We’re going to define our actions.

Here are the actions needed:

admin_print_scripts• and wp_print_scripts. These are needed to add in the JavaScript files.

wp_print_styles• and admin_print_styles. These are needed to load the plugin’s CSS.

admin_menu• . This is needed to add in our admin panel.

Let’s look at some code:

//instantiate the class if (class_exists('WPGrins')) { $GrinsLite = new WPGrins(); }

function __construct(){ if ( !defined('WP_CONTENT_URL') ) define( 'WP_CONTENT_URL', get_option('siteurl')

Page 130: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 129

. '/wp-content'); if ( !defined('WP_CONTENT_DIR')) define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' ); add_action('admin_print_scripts', array(&$this,'add_scripts'),1000); add_action('wp_print_scripts', array(&$this,'add_scripts'),1000); add_action('wp_print_styles', array(&$this,'wp_grins_head')); add_action('admin_print_styles', array(&$this,'wp_grins_head')); //Admin options add_action('admin_menu', array(&$this,'add_admin_pages')); $this->adminOptions = $this->get_admin_options(); }

The actions admin_print_scripts and wp_print_

scripts both have a callback method called add_scripts. The second group of actions wp_print_styles and admin_print_styles both have a callback method of wp_grins_head. The final action, admin_menu, has a callback method of add_admin_pages.

One other thing is going on here. We assign a class variable called adminOptions.

The adminOptions variable will contain an array of our various admin-panel options (which we’ll de-fine later). The adminOptions variable can be refer-enced using the $this predefined PHP variable.

Just looking at the constructor alone, we know we have to define the following methods:

Page 131: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax130

add_scripts•

wp_grins_head•

add_admin_pages•

get_admin_options•

We’ll also eventually have to add some query vari-able functions in order to use our Ajax processor.

Let’s work on the add_scripts method first.

add_scriptsThe add_scripts method is doing two duties: it’s loading JavaScript for both the front-end and ad-min section of the WordPress website.

We know we don’t want the scripts to be loaded where WP Grins isn’t needed. But where exactly do we want WP Grins to load?

Let’s do a little brainstorming. We obviously want WP Grins to load when a comment form is pres-ent (which are on single posts and pages on the front-end). There’s also a comment form on the back-end, so we want that too.

For posts, we want the scripts to load when some-one is creating or editing a post. Likewise for pages.

Page 132: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 131

Now let’s go over our dependencies:

Load only front-end pages where there is a •comment form (use is_single() and is_page() for detection).

Only load when a person is editing or creating •a post (use post.php and post-new.php).

Only load when a person is editing or creating •a new page (use page.php and page-new.php).

Load when someone is editing a comment in •the admin panel (use comment.php)

We also have one JavaScript dependency, which is jQuery.

Let’s move on to the code for add_scripts.

function add_scripts(){ global $post; if (is_admin()) { switch (basename($_SERVER['SCRIPT_FILENAME'])) { case "post.php"; case "post-new.php"; case "page.php"; case "page-new": case "comment.php": break; default: return; } } else if ((!is_single() && !is_page()) || 'closed' == $post->comment_status) { return; }

Page 133: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax132

wp_enqueue_script('wp_grins_lite', plugins_url('wp-grins-lite') . '/js/wp-grins.js', array("jquery"), 1.0); wp_localize_script( 'wp_grins_lite', 'wpgrinslite', $this->get_js_vars()); }

We first check to see if we’re in the admin panel. If we are, we search for our five pages. If we don’t find our pages, we exit the method.

The next conditional checks to see if we’re on a post or a page. If we happen to be on a post or page, but comments are turned off (no comment form), then we also exit.

We then queue our scripts and assign some local-ization via the get_js_vars method. So let’s move on to that method next.

get_js_varsThe get_js_vars method is straight to the point.

What we want is to determine, in JavaScript, whether we’re in the admin panel or on a post.

A second thing we need is our site’s URL for Ajax purposes.

Page 134: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 133

Straight and to the point, right?

One thing to point out is the reference to the class variable adminOptions (using $this->adminOptions). It’s calling a key called manualinsert. We will define this later when we retrieve our admin options.

Alright, we got the scripts down. Let’s now move on to the styles using the wp_grins_head method.

The third thing we need is to determine if the sm-ilies on a post are going to be inserted manually, or via JavaScript.

Let’s get to the code:

//Returns various JavaScript vars needed for the scripts function get_js_vars() { if (is_admin()) { return array( 'SITE_URL' => get_bloginfo('url'), 'LOCATION' => 'admin', 'MANUAL' => 'false' ); } return array( 'SITE_URL' => get_bloginfo('url'), 'LOCATION' => 'post', 'MANUAL' => $this->adminOptions['manualinsert'] ); } //end get_js_vars

Page 135: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax134

Quite simple really. We’re just declaring a cursor in the shape of a hand for all of the smilies.

wp_grins_headThe wp_grins_head (insert bad joke here) method will be used to insert the CSS for the smilies.

All we do with this method is print out the CSS in the WordPress’s header section. If you look at your header.php file in your theme, chances are it has a function call to wp_head.

This call enables us to load our JavaScript and CSS.

Without further ado, here is the code for wp_grins_head.

function wp_grins_head() { print(' <style type="text/css"> #wp_grins img { cursor: pointer; } </style> <!--[if IE]> <style type="text/css"> #wp_grins img { cursor: hand; } </style> <![endif]--> '); }

Page 136: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 135

We pass to the add_options_page function the page title, menu title (shown in the Settings section in the admin panel), the access level (in our case, ad-min only), the current file, and a callback method that will show the admin page.

The method printing out the admin page is print_admin_page. Let’s move on to that method next.

print_admin_pageOur print_admin_page method will perform one simple task: reference an external PHP file that holds our admin panel.

Let’s go ahead and work on our add_admin_pages method.

add_admin_pagesThe add_admin_pages is the callback method for the admin_menu action.

The code to add a menu is quite simple.

function add_admin_pages(){ add_options_page('WP Grins Lite', 'WP Grins Lite', 9, basename(__FILE__), array(&$this, 'print_admin_page')); }

Page 137: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax136

I like to keep my admin panel scripts separate just because it’s a little bit cleaner. I also like to edit my admin panel scripts without having to dig into the main plugin’s PHP file. It’s just my preference, and you are welcome to include the admin panel code directly in your class if desired.

Here’s our code:

We reference the external script admin-panel.php, which we’ll get to a bit later.

Now let’s work on the get_admin_options method.

get_admin_optionsThe get_admin_options method holds our default admin panel variables.

I’ve devised a clever function that checks for ex-istence of new keys, and keys that have been de-leted.

//Provides the interface for the admin pages function print_admin_page() { include dirname(__FILE__) . '/admin-panel.php'; }

Page 138: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 137

The above snippet might seem overly complicated for just one key, but it’s very scalable. I have up-wards of fifty keys for some of my plugins, and it

The result is a reusable snippet that one can use for their own theme or plugin script.

For our particular plugin, we only need to know the value of one variable, which is if the user has decided to manually insert the smilies on a post or a page.

We’ll use a key called manualinsert, and assign it a default value of “false”.

//Returns an array of admin options function get_admin_options() { if (empty($this->adminOptions)) { $adminOptions = array( 'manualinsert' => 'false' ); $options = get_option($this->adminOptionsName); if (!empty($options)) { foreach ($options as $key => $option) if (array_key_exists($key, $adminOptions)) { $adminOptions[$key] = $option; } } $this->adminOptions = $adminOptions; $this->save_admin_options(); } return $this->adminOptions; }

Page 139: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax138

We call the WordPress function update_option, and pass it our option name (defined as a class variable with value wpgrinslite) and our admin options.

wp_grins

works just fine, even when I add or delete keys for updated versions.

It also needs to be called only once, so it saves on database calls.

The final part of the code calls the method save_admin_options. Let’s move on to that method.

save_admin_optionsThe save_admin_options method does one task: it saves the admin options for later use (obvious, right?).

The code should just about explain itself.

//Saves for admin function save_admin_options(){ if (!empty($this->adminOptions)) { update_option($this->adminOptionsName, $this->adminOptions); } }

Page 140: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 139

The $tag variable holds the smily code that will be used in a post (e.g., :shock:). The $grin variable holds the filename (e.g., icon_redface.gif) to the smily.

For each $grin and $tag, we build an img tag.

We have a few methods to go before we’re done with the WPGrins class. For now, we need to work on outputting those beloved smilies.

There’s a WordPress PHP global variable called wpsmi-liestrans, which is an array of all of the smilies available.

What’s needed is a foreach loop that will build one long string. Once the string is built, we return it.

function wp_grins() { global $wpsmiliestrans; $grins = ''; $smiled = array(); foreach ($wpsmiliestrans as $tag => $grin) { if (!in_array($grin, $smiled)) { $smiled[] = $grin; $tag = str_replace(' ', '', $tag); $grins .= '<img src="'.get_bloginfo('wpurl').'/wp-includes/images/smilies/'.$grin.'" alt="'.$tag.'" onclick="jQuery.wpgrins.grin(\''.$tag.'\');"/> '; } } return $grins; } //end function wp_grins

Page 141: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax140

Did you notice that within the img tag there is an onclick reference? Hmmm, I wonder what that jQuery.wpgrins.grin() thing is?

I’m just guessing, but I think it’s a reference to a namspace called wpgrins, and a public function within that namespace called grin. Perhaps a hint of things to come?

We’re almost done with our WPGrins class. What’s left to do is add in query variable functionality in order to access our Ajax processor.

Add in Query Variable FunctionalityLet’s go ahead and build the methods load_page and query_trigger.

function load_page() { $pagepath = WP_PLUGIN_DIR . '/wp-grins-lite/'; switch(get_query_var('grins')) { case 'ajax-processor': include($pagepath . 'ajax-processor.php'); exit; default: break; } } //end function load_page function query_trigger($queries) { array_push($queries, 'grins'); return $queries; } //end function query_trigger

Page 142: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 141

We added a query called grins with a query vari-able named ajax-processor. Now the path to our Ajax processor will look like this:

http://www.yourdomain.com/?grins=ajax-processor

//instantiate the class if (class_exists('WPGrins')) { $GrinsLite = new WPGrins(); add_action('template_redirect', array(&$GrinsLite,'load_page')); add_filter('query_vars', array(&$GrinsLite,'query_trigger')); }

Up next is adding in the action and filter needed for the two methods.

So congratulations, we’re done with our WPGrins class. We still, however, have to add in our Word-Press template tag so that users can manually in-sert smilies.

Our Template TagLet’s go ahead and call our template tag wp_print_grins (yes, I’m unoriginal, and if you listen to my dad, quite lazy).

We’re going to use this template tag to access our class and return the grins to the end user.

Page 143: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax142

The above snippet is rather straight forward. We reference the class reference variable GrinsLite by using the PHP reserved word global.

We then check to see if the variable is set. If it is, we call the class method wp_grins and return the result to the user.

The user, when calling the template tag, would use:

if (function_exists('wp_print_grins')) { echo wp_print_grins(); }

Congratulations! We’re done with wp-grins.php. Let’s move on to the admin panel.

The Admin PanelThe first step to creating an external admin panel is to do a basic security check so that malicious users can’t access it directly.

if (!function_exists('wp_print_grins')) { function wp_print_grins() { global $GrinsLite; if (isset($GrinsLite)) { return $GrinsLite->wp_grins(); } } }

Page 144: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 143

We check for the existence of the class variable adminOptionsName. Accessing the file directly will cause the file to crash and burn.

Next, we check to see if the user is admin. This is perhaps unnecessary, but you can’t ever have too much security (airlines, anyone?).

<?php /* Admin Panel Code - */ if (empty($this->adminOptionsName)) { die(''); }

//Check to see if a user can access the panel if ( function_exists('current_user_can') && !current_user_can('edit_users') ) die("nope");

You can leave a nastier message if you like, but I chose something benign.

I assume the user is an admin-type if the user can edit users, which I believe is a valid assumption.

Next up is the code that will execute when a user clicks the “Update Settings” button. We’ll have to do a little foresight here and determine what should be updated.

Page 145: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax144

//Update settings if (isset($_POST['update'])) { check_admin_referer('wp-grins-lite_options'); $options['manualinsert'] = $_POST['manual']; $this->adminOptions = $options; $this->save_admin_options(); ?> <div class="updated"><p><strong><?php _e('Settings successfully updated.', $this->localizationName) ?></strong></p></div> <?php } ?>

First we’ll have to do a nonce check, so we know in advance we’ll have to include a nonce in our form field.

Second, we’ll have to update the options and show a success message.

Alright, we know what we want, so let’s get to the code.

We first check for the existence of a POST variable called update (which will be the name of our sub-mit button. Up second is our nonce check, which has a name of wp-grins-lite_options.

We then declare an associative array with a key called manualinsert and assign it the POST variable manual.

Page 146: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 145

Keep in mind we created the nonce field with a name of wp-grins-lite_options, which is the same name we used above in our nonce check.

The action attribute in our form is used to self-post to itself, so we expect that clicking on “Update Settings” will return us to the same page (the rea-son we have the update code on the same page).

Let’s get to work on our interface:

The last thing we do is display a success message to the user.

Now it’s time to work on our user interface. Sim-ple radio box options for the manual insert will suffice.

First, let’s add our nonce field to the form.

<div class="wrap"> <h2>WP Grins Lite Options</h2> <form method="post" action="<?php echo $_SERVER["REQUEST_URI"]; ?>"> <?php wp_nonce_field('wp-grins-lite_options') ?>

<table class="form-table"> <tbody> <tr valign="top"> <th scope="row"><?php _e('Manually insert grins? You will have to call wp_print_grins()', $this->localizationName) ?></th> <td><p><label for="manual_yes"><input type="radio" id="manual_yes" name="manual" value="true" <?php if

Page 147: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax146

Nothing spectacular is going on here. We out-put in HTML two radio boxes with the same name attribute. Once has a value of true, and the other false. When the user clicks on “Update Settings”, we can capture that value. We also do a condi-tional check using $options[‘manualinsert’] to check the correct radio box by default.

All that is left to do now is create our “Update Settings” button.

<div class="submit"> <input type="submit" name="update" value="<?php _e('Update Settings', $this->localizationName) ?>" /> </div> </form> </div>

($options['manualinsert'] == "true") { echo('checked="checked"'); }?> /> <?php _e('Yes',$this->localizationName); ?></label>&nbsp;&nbsp;&nbsp;&nbsp;<label for="manual_no"><input type="radio" id="manual_no" name="manual" value="false" <?php if ($options['manualinsert'] == "false") { echo('checked="checked"'); }?>/> <?php _e('No',$this->localizationName); ?></label></p></td> </tr> </tbody> </table>

That’s it! We’re done with our admin panel. Now it’s time to test it.

Page 148: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 147

Fantastic! It worked. Now let’s set the manual option back to “No”.

Since we have the admin panel up and running, let’s work on our JavaScript file.

The JavaScript File (wp-grins.js)The base for our wp-grins.js file will contain two functions: one to print out each grin as it is clicked (grin) and another to initiate our Ajax call to print out the grins where needed (init).

Our base code will look like the following:

WP Grins Admin PanelFigure 9.

jQuery(document).ready(function() { var $j = jQuery; $j.wpgrins = {

Page 149: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax148

Now the code for printing out each grin (using the grin function) in the appropriate text box is mon-strous. How about you just nod your head and assume that it “just works”? Okay, then (phew!).

What we’re really here for is Ajax anyways, so let’s get to the init function.

Before we get to the code, I defined the variable $j as our reference to jQuery. I also call the init function (using $j.wpgrins.init()) upon loading. Alright, let’s get to it.

init: function() { if (wpgrinslite.MANUAL == "true") { return; } var s = {}; s.response = 'ajax-response'; s.type = "POST"; s.data = $j.extend(s.data, {action: 'grins'}); s.global = false; s.url = wpgrinslite.SITE_URL + "?grins=ajax-processor"; s.timeout = 30000; s.success = function(r) { }

grin: function(tag) { }, init: function() { } }; $j.wpgrins.init(); });

Page 150: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 149

s.success = function(r) { var grinsDiv = '<div id="wp_grins">'+r+'</div>'; if ($j('#postdiv').length > 0) { var type = 'after'; var node = $j('#postdiv'); } else if ($j('#postdivrich').length > 0) { var type = 'after'; var node = $j('#postdivrich');

$j.ajax(s); }

The first thing we check for is if the user is go-ing to manually insert the smilies. This will be false in the admin panel (remember our localized JavaScript variables?), so no worries there. On a post, it will be what the user has decided upon in the admin panel.

We then build our s object and build a data object with an action called grins. You’ll find out later that an action is unneeded, but I am a creature of habit. After the s object is built, we make our Ajax call.

What we expect to receive back from our Ajax processor is a string of all the available smilies.

Via our success function, we will print out the string filled with smilies.

The string with our smilies is contained in vari-able r.

Page 151: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax150

} else if ($j('#comment').length > 0) { var type = 'before'; var node = $j('#comment'); } else { return; } switch (type) { case 'after': node.after(grinsDiv); $j("#wp_grins").css("paddingTop", "5px"); break; case 'before': node.before(grinsDiv); break; } }

We perform several conditional checks to see if we’re in a post view (in the admin panel). If so, we assign a variable named type with the value af-ter (since we want the smilies to appear after the edit area).

If we’re in a comment area, we assign type with value before.

Up next is a simple switch statement that deter-mines if the grins are displayed before or after each node. Again, if we’re in a comment area, the grins are displayed before the edit box. If we’re in a post area, the grins are displayed after the edit box.

Page 152: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 12, Example #1: WP Grins Lite 151

<?php header('Content-Type: text/html; charset=UTF-8'); define('DOING_AJAX', true); die(wp_print_grins()); ?>

Coolness! We’re done with our script.

Okay students, what are we expecting our Ajax processor to return? If you answered, “A string with our smilies”, then you are correct. If you an-swered something else, then it’s back to the bottle for you.

Alright then. To the Ajax processor we go!

The Ajax ProcessorThis Ajax processor is going to be the most com-plicated snippet you’ve seen yet.

Without further ado, here it is:

I know, huh? You’re probably scratching your monitor right now trying to get to me (good luck with that).

We simply call our public “template function” and return the string. No security checks needed here either.

Page 153: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax152

We’re done with Example Numero Uno.

So, take a breath, and when you’re ready to, move on and go to Example Number Two (ooh, two rhyming sentences in a row!).

As a reminder, all of the code mentioned in this ex-ample is included with this book.

Page 154: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13

Example #2: Static Random Posts

Page 155: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax154

Example #2: Static Ran-dom PostsThe WordPress Plugin Static Random Posts was built out of frustration with other “random posts” plugins.

I would often visit a site that had displayed ran-dom posts. If I clicked on one, I’m taken to the post. But if I click the back button, all of the posts are refreshed (unless the website makes use of a caching plugin).

To combat this refreshing problem, I thought to myself, “What if the random posts were static for a certain amount of time?”

Another requirement for me is that these random posts could be “refreshed” manually by the blog’s admin. For example, if I set the time to refresh for one week (10,080 minutes in a week), I could manually refresh the posts until I found a suitable combination that I liked.

After the brainstorming, I came up with the main requirements for the plugin:

The plugin would be a widget.•

Page 156: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 155

The plugin would have an option to set the •number of posts displayed and the title of the widget.

The plugin would have an admin panel to set •the refresh time (minutes) and to exclude cer-tain categories if necessary.

The plugin would have a “Refresh” link on the •blog’s front-end so that admin could refresh the posts at will.

Based on those requirements, we know we need an admin panel, a script to process the “Refresh” link, an Ajax processor to return the refreshed posts, and a main plugin file to create and process the widget.

js

static-random-posts.phpstatic-random-posts

static-random-posts.js

php admin-panel.php

ajax-processor.php

Static Random Posts File StructureFigure 10.

Page 157: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax156

Let’s get started on the static-random-posts.php file.

Creating the Static Random Posts WidgetLet’s first look at the class skeleton for our wid-get.

<?php if (!class_exists('static_random_posts')) { class static_random_posts extends WP_Widget { var $localizationName = "staticRandom"; var $adminOptionsName = "static-random-posts"; function static_random_posts(){ $this->adminOptions = $this->get_admin_options(); if ( !defined('WP_CONTENT_URL') ) define( 'WP_CONTENT_URL', get_option('siteurl') . '/wp-content'); if ( !defined('WP_CONTENT_DIR')) define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' ); //Initialization stuff add_action('init', array(&$this, 'init')); //Admin options add_action('admin_menu', array(&$this,'add_admin_pages')); //JavaScript add_action('wp_print_scripts', array(&$this,'add_post_scripts'),1000); //Create widget $widget_ops = array('description' => __('Shows Static Random Posts.', $this->localizationName) ); $this->WP_Widget('staticrandomposts', __('Static Random Posts', $this->localizationName), $widget_ops); } //end function static_random_posts

Page 158: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 157

Please note that we inherit the class WP_Widget, which will aid us in creating our widget.

From looking at the above code, we know we have several methods that need some work.

widget• - Displays the widget to the end user.

update• - Updates the widget when the admin modifies the widget’s settings.

form• - The widget interface in the admin pan-el.

init• - Will initialize our localization code.

/* init - Run upon WordPress initialization */ function init() { //* Begin Localization Code */ //* End Localization Code */ } //end function init // widget - Displays the widget function widget($args, $instance) { } //Updates widget options function update($new, $old) { } //Widget form function form($instance) { } } //End class } add_action('widgets_init', create_function('', 'return register_widget("static_random_posts");') ); ?>

Page 159: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax158

The form method is automatically passed an in-stance of the widget. We use the function wp_parse_args to merge the instance with our defaults.

Our defaults are as follows:

add_admin_pages• - Adds our admin panel.

add_post_scripts• - Loads the scripts we need.

Let’s get started on the form method first, which is what will be displayed to the admin when adding the widget.

formThe form method will be automatically called when the widget is added in the admin panel.

Within this method, we will create the user inter-face that the admin will see.

Let’s get started on this method’s initialization code:

//Widget form function form($instance) { $instance = wp_parse_args((array)$instance, array('title'=> __("Random Posts", $this->localizationName),'postlimit'=>5,'posts'=>'', 'time'=>'')); $postlimit = intval($instance['postlimit']); $posts = $instance['posts']; $title = esc_attr($instance['title']);

Page 160: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 159

title• - The title of the widget that will be dis-played on the blog’s front end.

postlimit• - How many posts to retrieve.

posts• - A comma separated string with our stored posts.

time• - The time the admin sets for post refresh-ing.

Creating the user interface will fall after the ini-tialization.

?> <p> <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e("Title", $this->localizationName); ?><input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" /> </label> </p> <p> <label for="<?php echo $this->get_field_id('postlimit'); ?>"><?php _e("Number of Posts to Show", $this->localizationName); ?><input class="widefat" id="<?php echo $this->get_field_id('postlimit'); ?>" name="<?php echo $this->get_field_name('postlimit'); ?>" type="text" value="<?php echo $postlimit; ?>" /> </label> </p>

<p><?php _e("Please visit",$this->localizationName)?> <a href="options-general.php?page=static-random-posts.php"><?php _e("Static Random Posts",$this->localizationName)?></a> <?php _e("to adjust the global settings",$this->localizationName)?>.</p>

Page 161: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax160

<?php } //End function form

When creating our labels and form inputs, notice that we call the class methods get_field_id and get_field_name and pass it the name of our defaults.

These methods echo out the unique IDs and form names necessary for the widget.

Widget User Interface Figure 11.

Page 162: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 161

When the admin clicks “Save”, a new and old in-stance is sent to the update method.

We simply assign the variable $instance with the $old instance. We then pass the $instance variable

Towards the end of our user interface, we provide a link to our global options, which is a link to our admin page (options-general.php?page-static-ran-dom-posts.php). This link is only for convenience and lets the admin know there are other options for the widget that affect all the widgets globally.

As you can see from the screenshot, there is a “Save” button. When the button is clicked, the update method runs, so let’s move on to that next.

updateThe update method performs one simple task: it updates the widget with the options the admin selects.

//Updates widget options function update($new, $old) { $instance = $old; $instance['postlimit'] = intval($new['postlimit']); $instance['title'] = esc_attr($new['title']); return $instance; }

Page 163: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax162

our new data and return the instance. Not much to it.

Let’s now work on our class variable $this-

>adminOptions, which gets initialized via the get_admin_options method.

get_admin_optionsThe get_admin_options method is practically iden-tical to the method we used in Example #1, with the exception of the default keys used.

Our defaults in this case are:

minutes• - Number of minutes until the posts are refreshed.

categories• - The categories of the posts we would like to exclude.

Both of these options will be used in the admin panel, as well as in several other methods in the class that we have yet to define.

Let’s get to the code.

//Returns an array of admin options function get_admin_options() { if (empty($this->adminOptions)) { $adminOptions = array( 'minutes' => '5', 'categories' => ''

Page 164: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 163

Again, we set the defaults, and merge the keys with any saved ones.

Since this method calls save_admin_options, let’s work on that next.

save_admin_optionsOnce again, this code is practically identicial to the method in Example #1.

); $options = get_option($this->adminOptionsName); if (!empty($options)) { foreach ($options as $key => $option) if (array_key_exists($key, $adminOptions)) { $adminOptions[$key] = $option; } } $this->adminOptions = $adminOptions; $this->save_admin_options(); } return $this->adminOptions; }

function save_admin_options(){ if (!empty($this->adminOptions)) { update_option($this->adminOptionsName, $this->adminOptions); } }

Page 165: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax164

The code is fairly simple. It takes the class vari-able $this->adminOptions and updates it according to our $this->adminOptionsName variable.

So what’s next?

How about we work on some of our actions.

initThe init method will perform our plugin local-ization. We called it by adding this action in our constructor: add_action(‘init’,array(&$this, ‘init’));

The only thing to note here is that all of the lo-calization files will be included in the “languages” sub-directory, which I’ve included in the final pl-ugin version.

/* init - Run upon WordPress initialization */ function init() { //* Begin Localization Code */ $static_random_posts_locale = get_locale(); $static_random_posts_mofile = WP_CONTENT_DIR . "/plugins/static-random-posts/languages/" . $this->localizationName . "-". $static_random_posts_locale.".mo"; load_textdomain($this->localizationName, $static_random_posts_mofile); //* End Localization Code */ } //end function init

Page 166: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 165

Let’s move onto the next action, which will use the add_post_scripts method.

add_admin_pagesThe next action is the action for adding in our admin page. Here is the action we used: add_action(‘admin_menu’, array(&$this,’add_admin_pag-

es’));

And here’s the resulting code:

function add_admin_pages(){ add_options_page('Static Random Posts', 'Static Random Posts', 9, basename(__FILE__), array(&$this, 'print_admin_page')); }

//Provides the interface for the admin pages function print_admin_page() { include dirname(__FILE__) . '/php/admin-panel.php'; }

Simple and to the point. We call the method print_admin_page, so let’s move onto that one next.

print_admin_pageThis method will output our admin panel. As with Example #1, I use an external PHP file for the admin panel.

Page 167: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax166

add_post_scriptsThe add_post_scripts method is called by the following action: add_action(‘wp_print_scripts’, array(&$this,’add_post_scripts’),1000);

What we want to do within this function is regis-ter our JavaScript files and do some simple detec-tion to ensure the scripts don’t load unless neces-sary.

What we’ll first do in this method is ensure that the widget is indeed being shown on the front-end.

We call the is_active_widget WordPress function to determine if the widget is being shown. If it isn’t, we get out of the method.

We then load our scripts.

function add_post_scripts() { if (is_active_widget(true, $this->id, $this->id_base) == false) { return; }

function add_post_scripts() { if (is_active_widget(true, $this->id, $this->id_base) == false) { return; } wp_enqueue_script('static_random_posts_script', $this->pluginDir . '/js/static-random-posts.js', array("jquery", "wp-ajax-response") , 1.0); wp_localize_script( 'static_random_posts_script',

Page 168: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 167

Since we will have a “Refresh...” button, we need a variable to hold that text. We also need a “Load-ing...” message for when the button is clicked. And we need our site’s URL for the query variable technique.

That’s it. Now let’s move on to the last action and filter we’ll need for the query variable technique.

function get_js_vars() { return array( 'SRP_Loading' => __('Loading...', $this->localizationName), 'SRP_Refresh' => __('Refresh...', $this->localizationName), 'SRP_SiteUrl' => get_bloginfo('url') ); } //end get_js_vars

Pretty straight forward. When we localize, we call the method get_js_vars. Let’s move on to that method next.

get_js_varsAll that we’re doing in the get_js_vars method is returning an array of variables that need to be lo-calized for use in our JavaScript file.

'staticrandomposts', $this->get_js_vars()); }

Page 169: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax168

We do this because there is no class variable to ref-erence. In other words, the class hasn’t been ex-plicitly instantiated, so we can’t access it directly.

function SRP_load_page() { $pagepath = WP_PLUGIN_DIR . '/static-random-posts/php/'; switch(get_query_var('SRP')) { case 'ajax-processor': include($pagepath . 'ajax-processor.php'); exit; default: break; } } //end function load_page function SRP_query_trigger($queries) { array_push($queries, 'SRP'); return $queries; } //end function query_trigger add_action('template_redirect', 'SRP_load_page'); add_filter('query_vars', 'SRP_query_trigger'); add_action('widgets_init', create_function('', 'return register_widget("static_random_posts");') );

Setting Up Our Query VariablesSince the class isn’t being explicitly instantiated, we need to load our query variable functions out-side of the class. To load our Ajax processor, we’ll use a query variable of SRP and point it to ajax-processor.

Instead of referencing a class variable, we just include local functions with a prefix in order to minimize the potential for name conflicts.

Page 170: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 169

The first thing we do is extract the $args array us-ing the PHP function extract.

The $args array is filled with some useful keys. By using the extract function, we now have the fol-lowing variables available:

$before_title• - What code and/or words to display before the widget title.

That’s it. We’re done with our actions and fil-ters.

Now let’s move onto the widget method, which will display the random posts on the front-end.

widgetThe widget method is automatically called when displaying the widget on the front-end.

We get passed two variables:

$args• - An array of arguments.

$instance• - The instance of the widget.

The beginning of the method will look like this:

// widget - Displays the widget function widget($args, $instance) { extract($args, EXTR_SKIP);

Page 171: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax170

$after_title• - What code and/or words to dis-play after the widget title.

$before_widget• - What code and/or words to display before the widget.

$after_widget• - What code and/or words to display after the widget.

As a plugin author, we can ignore these variables, but in order to ensure maximum theme compat-ibility, using these variables is a must.

A themer defines these variables somewhere in their theme (most likely in functions.php) by reg-istering a sidebar (example):

if ( function_exists('register_sidebar') ) register_sidebar(array( 'name' => 'Sidebar1', 'before_widget' => '<div class="sidebar-box"><div class="sidebar-box-inside">', 'after_widget' => '</div></div>', 'before_title' => '<h3>', 'after_title' => '</h3>', ));

We have to assume that the themer knows what he’s doing and rely on his code for the look and feel of our widget.

Let’s look at the rest of this code for the widget method.

Page 172: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 171

There’s four major things going on here in this method:

We add in the default variables before/after •the widget and before/after the title.

function widget($args, $instance) { extract($args, EXTR_SKIP); echo $before_widget; $title = empty($instance['title']) ? __('Random Posts', $this->localizationName) : apply_filters('widget_title', $instance['title']); if ( !empty( $title ) ) { echo $before_title . $title . $after_title; }; //Get posts $post_ids = $this->get_posts($instance); if (!empty($post_ids)) { echo "<ul class='static-random-posts' id='static-random-posts-$this->number'>"; $this->print_posts($post_ids); echo "</ul>"; if (current_user_can('edit_users')) { $refresh_url = clean_url( wp_nonce_url(get_bloginfo('url') . "/?SRP=ajax-processor&action=refreshstatic&number=$this->number&name=$this->option_name", "refreshstatic_$this->number")); echo "<br /><a href='$refresh_url' class='static-refresh'>" . __("Refresh...",$this->localizationName) . "</a>"; }

} echo $after_widget; } //end widget

Page 173: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax172

We retrieve the post IDs to display by calling •the get_posts method. We pass it the current $instance variable.

We output an unordered list and call the • print_

posts method with the list of our post IDs.

If the user is admin, we output a link to our •Ajax processor for refreshing the random posts.

When all is said and done, our front-end output should be a widget filled with random links.

Figure 12.Static Random Posts Output

Page 174: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 173

We have three more methods to go before we are done with the class. Let’s go over them quickly:

get_posts• - Returns a comma separated string of post IDs to include in our output.

build_posts• - Returns an instance that includes the post IDs and the time of when the posts will need to be refreshed.

print_posts• - Prints out the posts to the front-end.

get_postsThe get_posts method is what we use to return a string of post IDs.

It takes in two arguments:

$instance• - The widget instance.

$build• (true or false) - Whether to build new posts or not.

Before we get into the code, let me explain the main functionality of this method.

We retrieve the number of posts to display. We then do a quick check to see if there are stored post IDs. If not, we build the IDs.

Page 175: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax174

We then check to see if the time has expired for the static random links. If the time has expired, we build new random links.

We then check to see if the $build variable is true. If it is, we build the posts.

In all three conditionals, we update the main in-stance.

Let’s get to the code:

//Returns the post IDs of the posts to retrieve function get_posts($instance, $build = false) { //Get post limit $limit = intval($instance['postlimit']); $all_instances = $this->get_settings();

//If no posts, add posts and a time if (empty($instance['posts'])) { //Build the new posts $instance = $this->build_posts($limit,$instance); $all_instances[$this->number] = $instance; update_option( $this->option_name, $all_instances ); } else if(($instance['time']-time()) <=0) { //Check to see if the time has expired //Rebuild posts $instance = $this->build_posts($limit,$instance); $all_instances[$this->number] = $instance; update_option( $this->option_name, $all_instances ); } else if ($build == true) { //Build for the heck of it $instance = $this->build_posts($limit,$instance);

Page 176: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 175

Pay attention to the variable $all_instances. This variable holds all of the widgets.

When we are updating our instance, we update with this code:

We have to assume that the build_posts method updates the $instance[‘posts’] key. We then up-date only our widget by using:

$instance = $this->build_posts($limit,$instance); $all_instances[$this->number] = $instance; update_option( $this->option_name, $all_instances );

$all_instances[$this->number] = $instance;

$all_instances[$this->number] = $instance; update_option( $this->option_name, $all_instances ); } if (empty($instance['posts'])) { $instance['posts'] = ''; }

return $instance['posts']; }

Page 177: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax176

Finally, we call the WordPress function update_op-tion and pass it our widget’s option_name, and pass it all of the instances.

Since there is such heavy use of the build_posts method, let’s move onto that method next.

build_postsThe build_posts method takes in two arguments:

$limit• - The number of posts to retrieve.

$instance• - The instance to update and return.

function build_posts($limit, $instance) { //Get categories to exclude $cats = @implode(',', $this->adminOptions['categories']); $posts = get_posts("cat=$cats&showposts=$limit&orderby=rand"); //get posts by random $post_ids = array(); for ($i=0; $i<$limit; $i++) { $post_ids[$i] = $posts[$i]->ID; } $post_ids = implode(',', $post_ids); $instance['posts'] = $post_ids; $instance['time'] = time()+(60*intval($this->adminOptions['minutes'])); return $instance; }

Page 178: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 177

First, we get our saved categories IDs as specified in the admin panel options. Since the categories are in an array, we implode them and separate the categories by a comma. The $cats variable will hold all of the category IDs to exclude (i.e., -3,-4,-5).

We then retrieve our list of posts using the Word-Press function get_posts. We pass it our categories to exclude, the limit on posts, and the rand option (for our posts to be random).

After that, we build our $post_ids array, and im-plode this array to be a comma-separated string.

We update our instance with the $post_ids and a new time.

Finally, we return the instance.

print_postsThe print_posts method is our final method.

This method was called in the widget method when outputting the random posts.

echo "<ul class='static-random-posts' id='static-random-posts-$this->number'>"; $this->print_posts($post_ids); echo "</ul>";

Page 179: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax178

As you can see from the code, it retrieves the posts, and goes through them one-by-one to build a string full of posts.

If the $echo variable is set to true (default), it echoes the string rather than returning it.

The $echo variable will come in handy when we’re using our Ajax processor to retrieve the list of posts.

//Prints or returns the LI structure of the posts function print_posts($post_ids,$echo = true) { if (empty($post_ids)) { return ''; } $posts = get_posts("include=$post_ids"); $posts_string = ''; foreach ($posts as $post) { $posts_string .= "<li><a href='" . get_permalink($post->ID) . "' title='". $post_title ."'>" . htmlspecialchars(stripslashes(strip_tags($post->post_title))) ."</a></li>\n"; } if ($echo) { echo $posts_string; } else { return $posts_string; } }

The print_posts method takes a string of comma-separated post IDs. It then retrieves a list of posts based on those IDs, and outputs the result.

Page 180: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 179

We’re now done with creating the widget! Let’s move on to the admin panel.

The Admin PanelThere are three things we wish to accomplish with our admin panel for this example.

Allow the user to select a time (in minutes) for •the random posts to be refreshed.

Allow a user to select the categories he wants •to exclude when retrieving the random posts.

Update the time and categories and save them •to our admin options.

First, let’s deal with security:

<?php /* Admin Panel Code - Created on April 19, 2008 by Ronald Huereca Last modified on October 24, 2009 */ if (empty($this->adminOptionsName)) { die(''); } $options = $this->adminOptions(); //global settings //Check to see if a user can access the panel if ( function_exists('current_user_can') && !current_user_can('manage_options') ) die("nope");

Page 181: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax180

Now that we have the introduction of our file over with, let’s get to updating the options.

Updating the OptionsThe first obstacle we’ll tackle is that of time. We want to ensure that the time entered is numeric, and that the time is greater than one minute (not set to zero).

But first, we need to check our nonce, which uses the string static-random-posts_admin-options.

//Update settings if (isset($_POST['update'])) { check_admin_referer('static-random-posts_admin-options'); $error = false; $updated = false;

We check for the existence of $_POST[‘update’], check our nonce, and set defaults for variables $error and $updated.

Up next is checking the $_POST[‘time’] variable.

We use a regular expression to ensure that all values are a digit (you could have also used the PHP is_num function). If the time has non-numerical values, we set the error message.

Page 182: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 181

//Validate the time entered if (isset($_POST['time'])) { $timeErrorMessage = ''; $timeClass = 'error'; if (!preg_match('/^\d+$/i', $_POST['time'])) { $timeErrorMessage = __("Time must be a numerical value",$this->localizationName); $error = true; } else if($_POST['time'] < 1) { $timeErrorMessage = __("Time must be greater than one minute.",$this->localizationName); $error = true; } else { $options['minutes'] = $_POST['time']; $updated = true; } if (!empty($timeErrorMessage)) { ?> <div class="<?php echo $timeClass;?>"><p><strong><?php _e($timeErrorMessage, $this->localizationName);?></p></strong></div> <?php } }

We next check to see if the time value is less than one. If it is, we set the error message.

After passing the first two conditionals, we assume we’re good and update the $options array with the new time.

Page 183: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax182

//categories (add a "-" sign for exclusion) for ($i=0; $i<sizeof($_POST['categories']); $i++) { $_POST['categories'][$i] = "-" . $_POST['categories'][$i]; } $options['categories'] = $_POST['categories'];

SRP Error MessageFigure 13.

If the $timeErrorMessage isn’t empty (one of the first two conditionals has failed), then an error message is spit out for the user to see.

Up next is building our categories. Since we want to “exclude” them, we need to add a minus (-) sign before each one.

To perform this exclusion, we do a for loop and update each item in the array with a minus (-) sign. After the loop, we assign our $options array the new values.

Page 184: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 183

Finally, we save the options and output a “success” message.

$updated = true; if ($updated && !$error) { $this->adminOptions = $options; $this->save_admin_options(); ?> <div class="updated"><p><strong><?php _e('Settings successfully updated.', $this->localizationName) ?></strong></p></div> <?php } } ?>

Now it’s time to work on the user interface.

The User InterfaceThe user interface will contain a simple text box to enable users to enter the minutes desired before the random posts are refreshed.

Success MessageFigure 14.

Page 185: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax184

<div class="wrap"> <h2>Static Random Posts Options</h2> <form method="post" action="<?php echo $_SERVER["REQUEST_URI"]; ?>"> <?php wp_nonce_field('static-random-posts_admin-options') ?>

<table class="form-table"> <tbody> <tr valign="top"> <th scope="row"><?php _e('Set refresh time (minutes):', $this->localizationName) ?></th> <td><input type="text" name="time" value="<?php echo $options['minutes'] ?>" id="comment_time"/><p><?php _e('Your random posts will be refreshed every', $this->localizationName); echo " " . $options['minutes'] . " ";_e('minutes.', $this->localizationName);?></p></td> </tr>

Additionally, we’ll output rows of checkboxes that will contain the blog’s categories. Be default, none of these will be checked, but we’ll have to remember previously checked boxes.

First, let’s insert our nonce field.

Keep in mind we use the same exact string that we used in our nonce check: static-random-posts_admin-options.

Up next is our input box for the refresh rate (min-utes).

Page 186: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 185

Now it’s time to show the categories as rows of checkboxes.

What we want here is to get all of the categories as a flat list (no hierarchy). We also want to check each category and see if it is in our “exclude” op-tions. If it is, we want to check the checkbox.

After that, we want to print out the categories.

The result will look like the following figure:

Refresh Time Input BoxFigure 15.

<tr valign="top"> <th scope="row"><?php _e('Exclude Categories:', $this->localizationName) ?></th> <td> <?php $args = array( 'type' => 'post', 'child_of' => 0, 'orderby' => 'name', 'order' => 'ASC', 'hide_empty' => false, 'include_last_update_time' => false, 'hierarchical' => 1);

Page 187: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax186

The output of this code will look similar to this figure:

$categories = get_categories( $args ); foreach ($categories as $cat) { $checked = ''; if (is_array($options['categories'])) { if (in_array("-" . $cat->term_id, $options['categories'], false)) {

Finally, we print out our “Update” button.

Categories OutputFigure 16.

Page 188: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 187

Phew! Done with the admin panel!

Let’s next move on to our JavaScript file.

The JavaScript File (static-random-posts.js)The JavaScript file will perform one important function: it must capture when the “Refresh...” button is clicked and initiate an Ajax call. Let’s look at the base code for this file.

What we can tell from the code shown above is that a namespace of staticrandomposts is created.

<div class="submit"> <input type="submit" name="update" value="<?php _e('Update Settings', $this->localizationName) ?>" /> </div> </form> </div>

jQuery(document).ready(function() { var $j = jQuery; $j.staticrandomposts = { init: function() { initialize_links(); } }; //Initializes the refresh links function initialize_links() { } $j.staticrandomposts.init(); });

Page 189: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax188

Our link is pointing towards our Ajax processor (named ajax-processor.php) and contains four variables:

action=refreshstatic• - The action we are tak-ing.

number=12• - The unique number of our widget (this could be anything).

There’s a public function called init, and this function is called immediately upon a page load.

The init function calls the private function ini-tialize_links, so we have to assume the initial-ize_links function will contain the code to initial-ize our “Refresh...” button and to perform and receive the Ajax request.

Let’s start by writing the code to capture button clicks within the initialize_links function.

First, let’s go back to the “Refresh...” link that the user will click on. Here is the full path that is generated for the link:

http://your-domain.com/?SRP=ajax-processor&action=refreshstatic&number=12&name=widget_staticrandomposts&_wpnonce=3f1eee8a4f

Page 190: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 189

name=widget_staticrandomposts• - This is the name of our widget.

_wpnonce=3f1eee8a4f• - The unique nonce for the link.

Within our script, we must capture all of these variables and pass them onto the Ajax processor.

Now let’s get back to the event capturing for our button (err, link).

Our link (in case I didn’t mention it before) has a class attribute with the value of static-refresh. We bind a click event to it, and return false (mak-ing the link at this point utterly useless).

Once inside the click event function, we need to capture the four variables mentioned previously, build our s object, and initiate our Ajax call.

function initialize_links() { $j(".static-refresh").bind("click", function() { return false; }); }

//Initializes the refresh links function initialize_links() { $j(".static-refresh").bind("click", function() { //prepare object for AJAX call

Page 191: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax190

As shown in the above code, we first assign the “clicked” link to a variable named obj.

We alter the link’s inner-HTML by using one of our localized variables.

We then start building our s object, and create the s.data object with our four variables. One thing to note here is that we assign our passed _wpnonce variable to _ajax_nonce (for use in the Ajax proces-sor).

var obj = $j(this); obj.html(staticrandomposts.SRP_Loading);

var s = {}; s.element = obj.attr("class"); s.response = 'ajax-response'; var url = wpAjax.unserialize(obj.attr('href')); s.type = "POST"; s.data = $j.extend(s.data, {action: url.action, number: url.number, name: url.name, _ajax_nonce: url._wpnonce}); s.global = false; s.url = staticrandomposts.SRP_SiteUrl + "/?SRP=ajax-processor"; s.timeout = 30000; s.success = function(r) { } $j.ajax(s); return false; }); }

Page 192: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 191

A s.success placeholder is used, and then we initi-ate our Ajax call.

We’re done right? Not quite. We still have to build the inner-workings of the s.success func-tion.

First, what are we expecting here? When the user clicks the “Refresh...” link, he’s expecting new posts. So we can reasonably expect that the Ajax processor will return a string of these new posts ready for us to insert onto the front-end.

Our first goal upon receiving a response is to change the text back to “Refresh...” and parse the Ajax response.

After we have parsed the response, we go through each of the responses.

What we’re looking for in this case is a what value set to posts. We’ll then update the widget with the new “data”.

s.success = function(r) { obj.hide(); obj.html(staticrandomposts.SRP_Refresh); //Parse the XML response var res = wpAjax.parseAjaxResponse(r, s.response,obj.attr("class"));

Page 193: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax192

Now let’s think waaaay back (ok, only a few pages) to when we created the code to output the widget. We created an unordered list and filled this list with our posts.

Still don’t remember? Okay, here’s a refresher:

$j.each( res.responses, function() { if (this.what == "posts") { var data = this.data; $j("#static-random-posts-" + url.number).hide("slow", function() { $j("#static-random-posts-" + url.number).html(data); $j("#static-random-posts-" + url.number).show("slow", function() { obj.show(); }); return; }); } });

echo "<ul class='static-random-posts' id='static-random-posts-$this->number'>";

So when you see in the code $j(‘#static-random-posts-’ + url.number, that’s exactly what we’re us-ing.

We first hide this unordered list. We update its inner-HTML with the new data. We then show the

Page 194: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 193

unordered list, and make our “Refresh...” link vis-ible via a callback.

And that’s pretty much it.

Here’s the entire intialize_links function:

function initialize_links() { $j(".static-refresh").bind("click", function() { //prepare object for AJAX call var obj = $j(this); obj.html(staticrandomposts.SRP_Loading); var s = {}; s.element = obj.attr("class"); s.response = 'ajax-response'; var url = wpAjax.unserialize(obj.attr('href')); s.type = "POST"; s.data = $j.extend(s.data, {action: url.action, number: url.number, name: url.name, _ajax_nonce: url._wpnonce}); s.global = false; s.url = staticrandomposts.SRP_SiteUrl + "/?SRP=ajax-processor"; s.timeout = 30000; s.success = function(r) { obj.hide(); obj.html(staticrandomposts.SRP_Refresh); //Parse the XML response var res = wpAjax.parseAjaxResponse(r, s.response,obj.attr("class")); $j.each( res.responses, function() { if (this.what == "posts") { var data = this.data; $j("#static-random-posts-" + url.number).hide("slow", function() { $j("#static-random-posts-" + url.number).html(data); $j("#static-random-posts-" + url.number).show("slow", function() { obj.show(); });

Page 195: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax194

return; }); } }); } $j.ajax(s); return false; }); }

Cool beans! Now let’s move on to the Ajax pro-cessor.

The Ajax ProcessorThe first part of our Ajax processor will be setting the appropriate headers.

<?php header ('HTTP/1.1 200 OK'); header('Content-Type: text/html; charset=UTF-8'); define('DOING_AJAX', true);

When testing this out, the Ajax processor would erroneously return a 404 error even though the file was indeed there. To combat this, I added an ad-ditional header to return a 200 OK code.

Up next, we need to instantiate our static_ran-dom_posts class since it wasn’t instantiated in our main plugin file.

Page 196: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 195

We’ll assign the class reference to a variable named $staticrandomposts.

Now it’s time to read in our passed POST variables and perform an Ajax nonce check.

$staticrandomposts = new static_random_posts();

if (isset($_POST['number'])) { $number = intval($_POST['number']); $action = addslashes(preg_replace("/[^a-z0-9]/i", '', strip_tags($_POST['action']))); $name = addslashes(preg_replace("/[^_a-z0-9]/i", '', strip_tags($_POST['name']))); check_ajax_referer($action . "_" . $number);

With our $number and $name variables at hand, we have enough information to retrieve our widget, so let’s do that next.

//Get the widgets $settings = get_option($name); $widget = $settings[$number];

First, we get the widgets based on our widget name (there could be more than one of our wid-gets displayed).

Page 197: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax196

We’ve gone over the build_posts method before, so we know it returns an instance of the updated widget with brand new posts.

We then assign $post_ids with the new posts for later use.

We now need to save the widget and refresh the cache (if available).

We then narrow down the selection to just one widget by using our $number variable with the $settings array.

Our next task is to build the new posts:

//Save the settings $settings[$number] = $widget; update_option($name, $settings); //Let's clean up the cache //Update WP Super Cache if available if(function_exists("wp_cache_clean_cache")) { @wp_cache_clean_cache('wp-cache-'); }

//Get the new post IDs $widget = $staticrandomposts->build_posts(intval($widget['postlimit']),$widget); $post_ids = $widget['posts'];

Page 198: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 13, Example #2: Static Random Posts 197

We assign the data key with the result of the print_posts method (which we pass our $post_ids variable). The false we send tells the method not to echo out the variables and return them as a string.

Back in our JavaScript file, we accessed the data by using this line of code:

Now it’s time to build and return our Ajax re-sponse.

//Build and send the response $response = new WP_Ajax_Response(); $response->add( array( 'what' => 'posts', 'id' => $number, 'data' => $staticrandomposts->print_posts($post_ids, false))); $response->send();

} die(''); ?>

$j("#static-random-posts-" + url.number).html(this.data);

Page 199: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax198

Static Random Posts ConclusionWithin this chapter you learned how to create a WordPress widget with Ajax capabilities.

I walked you through the brainstorming process to determine what requirements are needed.

So you see how it’s all falling into place like a puzzle orgi? Okay, maybe I shouldn’t have gotten that descriptive, but you hopefully get the gist of it (it being Ajax and WordPress) now, yes?

Hopefully, because I only have one more example left. And (gasp), it’s for themers.

Let’s move on to some PayPal goodness and figure out how to add a dynamic PayPal Buy Now but-ton with a coupon feature.

Note: as with the previous example, the code for this example is included with the e-book.

Page 200: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14

Example #3: Dynamic PayPal

Buy Now Options With Coupon Codes

Page 201: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax200

Example #3: Dynamic Pay-Pal Buy Now Options With Coupon CodesNow here’s something you won’t read about ev-ery day: how to add “dynamic” PayPal Buy Now buttons. And you also get to read about using coupon codes (which PayPal doesn’t support by-the-way).

So we have to do what most people do when they want to accomplish something tricky: we fake it. And we’re going to use Ajax to perform this behind-the-scenes trickery.

Since I’ve never done this before (and chances are, you haven’t either), then we’re going to both be learning along the way.

The topics we’re going to cover will be:

Setting up the user interface.•

Adding our script file.•

Processing our Ajax request (via an Ajax pro-•cessor).

Setting up a simple Instant Payment Notifica-•tion (IPN) script.

Page 202: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 201

There’s several items of reading material you may want to set aside for reference (all are long and boring).

Website Payments Standard Integration Guid• e (PDF)

Instant Payment Notification Guid• e (PDF)

Sandbox User Guid• e (PDF)

HTML Variables for Website Payments Pr• o (HTML)

Instant Payment Notification Variable• s (HTML)

HTML Form Basics for Website Payments •Pro (HTML)

If you think that’s a lot of information, you’re not alone. It’s monstrous! It would take (at least) an-other book to cover all of those references alone. So I’m going to try to skip past the bull and tell you only what you need to know for this exam-ple.

If you want to expand on the example, you’re on your own (but you will have a good starting point).

Page 203: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax202

Setting Up the User InterfaceThe first step in our PayPal endeaver is creating our user interface.

First, we need to decide what we’re selling.

How about a t-shirt that has three sizes? And to make things interesting, let’s make each size have a different price.

Second, we need to make some assumptions:

Let’s assume there’s some pie-in-the-sky da-•tabase (nicknamed the Holy Database) that keeps track of the inventory.

This database holds the unique IDs for the •three sizes and the prices.

Now let’s set those unique IDs and prices.•

Unique ID Size Price

100 small $10.00

101 medium $15.00

102 large $20.00

Great! Now we have a foundation for the user interface.

Page 204: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 203

Based on the table, it looks like we’ll have a drop-down box and a “Buy Now” button for our inter-face. For each item in the drop-down, we need to somehow store the unique ID for later use (in order to determine price on the back-end of things).

Let’s move to the WordPress admin panel and cre-ate a new page. Let’s call this page “Purchase”.

Now, when creating this page, make sure you are in the HTML view (if you don’t, the stars will fall and Armageddon will commense).

For your convenience, here’s what everything should look like at this point.

The Purchase pageFigure 17.

Page 205: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax204

Now it’s time to insert some groovy HTML (yes, I said groovy) to represent our user interface.

We could easily use PayPal’s button creation tool (located on PayPal’s site under Merchant Servic-

es->Create Button), but since we’re creating a dy-namic “Buy Now” button experience, let’s use this quick-and-dirty code instead.

<form action="https://www.paypal.com/cgi-bin/webscr" method="post"> <table> <tr> <td>Sizes</td> <td> <select name="sizes"> <option value="100">Small $10.00</option> <option value="101">Medium $15.00</option> <option value="102">Large $20.00</option> </select> </td> </tr> <tr> <td>Coupon Code</td> <td><input type="text" name="couponcode" /></td> </tr> </table> <input type="hidden" name="cmd" value="_s-xclick"/> <input type="hidden" name="encrypted" /> <input type="image" src="https://www.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif" border="0" name="submit" id="paypal_submit" alt="PayPal - The safer, easier way to pay online!" style="padding-top:10px;"/> <img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1"/> </form>

Page 206: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 205

Pay close to attention to the code above because we’ll be using a lot of stuff from it. Here’s what we’ll be using in the future.

Each • option tag has a value equal to its unique ID. We’ll be using these IDs on the back-end.

There is an empty • input tag with a name of en-crypted. We’ll use this to place our completed PayPal data.

There’s a • submit button with an ID of paypal_submit. We’ll need to capture the click event for this button.

Before we move on, here’s a screenshot of what our interface currently looks like.

Purchase ScreenshotFigure 18.

Page 207: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax206

As a reminder, the new page should be published with a page slug of “purchase”. This is important since we’ll be using that during page detection for our upcoming JavaScript file.

Alrighty, we got the interface down. Let’s move on to adding in the script to capture the “Buy Now” button click.

Adding Our Script FileLet’s open up our theme’s functions.php file (if there isn’t one, create it).

Within this file, we’ll add an action for wp_print_scripts and point it to a local function called pay-pal_add_scripts.

Let’s assume our script file is called paypal_script.js. Let’s go ahead and create the blank file now in your theme’s main directory.

Back in our paypal_add_scripts function, let’s add in some basic page detection and queue our script

<?php add_action('wp_print_scripts', 'paypal_add_scripts'); function paypal_add_scripts() { } ?>

Page 208: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 207

with jquery and wp-ajax-response as dependencies.

If we’ve done everything correctly, the paypal_script.js file should be loading when we visit our purchase page.

Let’s now move on to the script file. Let’s create a namespace called paypalscript with a public initial-ization function (called init).

<?php add_action('wp_print_scripts', 'paypal_add_scripts'); function paypal_add_scripts() { if (is_page('purchase')) { wp_enqueue_script('paypal_script', get_bloginfo('template_url') . '/paypal_script.js', array('jquery','wp-ajax-response'),'1.0'); } } ?>

jQuery(document).ready(function() { var $ = jQuery; $.paypalscript = { init: function() { } }; $.paypalscript.init(); });

We’ll use this initialization function to capture our button click. From there, we’ll capture some variables from our user interface.

Here’s what we have so far:

Page 209: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax208

I’ve thrown an alert in there to test the button event. If you see an alert when clicking the but-ton, we’re good so far.

jQuery(document).ready(function() { var $ = jQuery; $.paypalscript = { init: function() { $("input[name='paypal_submit']").bind("click", function() { //Capture UI variables alert("Working"); return false; }); } }; $.paypalscript.init(); });

The Alert Figure 19.Message

Let’s remove the alert and create a variable (let’s call it selection) to hold the currently selected option.

You can read more on making a selection of form variables via the jQuery Selectors documentation.

Hopefully, however, the code will speak for itself.

Page 210: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 209

I’ve placed another alert in there to show us which option is selected. The alert should show us the unique ID of the option selected.

$("input[name='paypal_submit']").bind("click", function() { //Capture UI variables var selected = $("select[name='sizes'] option:selected").attr("value"); alert(selected); return false; });

The Unique IDFigure 20.

Have I mention before that alerts are a good debugging tool?

Up next is assigning a variable to capture the coupon code. Let’s call this variable coupon.

$("input[name='paypal_submit']").bind("click", function() { //Capture UI variables var selected = $("select[name='sizes'] option:selected").attr("value"); var coupon = $.trim($("input[name='couponcode']").

Page 211: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax210

That’s pretty much all the information we need from the user interface.

Let’s now work on creating our Ajax object (we’ll call it ajax instead of s this time just to show you can call it whatever you want).

However, to make this Ajax object, we need our theme’s directory (doh!).

Let’s go back into our functions.php file and add in a line of localization.

Alright, now we have all the things we need to build our Ajax object.

function paypal_add_scripts() { if (is_page('purchase')) { wp_enqueue_script('paypal_script', get_bloginfo('template_url') . '/paypal_script.js', array('jquery','wp-ajax-response'),'1.0'); wp_localize_script( 'paypal_script', 'paypalscriptvars', array('ThemeURL' => get_bloginfo('template_url'))); } }

attr("value")); alert(selected); return false; });

Page 212: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 211

What we plan to send via our Ajax request is sim-ply the unique ID of the selection, and the cou-pon code.

We’ll have to assume our Holy Database will be able to take this ID and spit out all of the other information we need (such as pricing).

And since I’m a visual person, here’s a treat: the Holy Database herself.

The Holy DatabaseFigure 21.

What can I say? I’m a sucker for cheap stock pho-tography.

Page 213: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax212

As shown above in the code, we provide a URL to our Ajax processor. Please note that we aren’t us-ing the query variable technique to load our Ajax processor since this example is designed to be used on your own site (hence, you should know where wp-load.php is located).

$("input[name='paypal_submit']").bind("click", function() { //Capture UI variables var selected = $("select[name='sizes'] option:selected").attr("value"); var coupon = $.trim($("input[name='couponcode']").attr("value")); var ajax = {}; ajax.element = $(this); ajax.response = 'ajax-response'; ajax.type = "POST"; ajax.url = paypalscriptvars.ThemeURL + "/ajax-processor.php"; ajax.data = $.extend(ajax.data, { coupon:coupon,id: selected}); ajax.global = false; ajax.timeout = 30000; ajax.success = function(r) { alert("Success"); }; $.ajax(ajax); return false; });

Let’s move on. Let’s begin by creating our Ajax object and creating an empty success function.

Page 214: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 213

If you haven’t already, create a blank ajax-proces-sor.php file and place it in your theme’s main di-rectory.

Now when you click on the “Buy Now” button, you should be met with a “Success” alert mes-sage.

See it? Good.

Now, what do we expect our Ajax processor to re-turn to us? Let’s think about this for a minute. We have a basic user interface with absolutely no PayPal information besides a “Buy Now” button.

So, perhaps we should expect this PayPal infor-mation in the form of a long string. We also have an empty input tag with the name attribute of en-crypted, so that’s probably where the data is going to go.

We also pass our Ajax processor a coupon code. If this code is invalid, we should expect our Ajax processor to let us know.

In our success function, we need to be prepared for both of these possibilities.

Let’s begin by parsing the Ajax response.

Page 215: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax214

Simple enough. Now let’s set up a $.each loop to prepare for our responses.

Up next is a switch statement to prepare for our two different possibilities.

ajax.success = function(r) { var res = wpAjax.parseAjaxResponse(r, this.response,this.element); $.each( res.responses, function() { //Prepare for the responses }); };

$.each( res.responses, function() { //Prepare for the responses switch(this.what) { case "error": return; break; case "paypal_data": return; break; } //end switch }); //end each

The switch statement looks for two possibilities in the this.what variable: error (if there is an error

ajax.success = function(r) { var res = wpAjax.parseAjaxResponse(r, this.response,this.element); }

Page 216: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 215

The output, if someone enters an invalid code, should look like this:

present) and paypal_data (the returned data).

The return statements are placed in each case in order to break out of the $.each loop.

Let’s work on the error message first. Let’s place an error message immediately after the coupon code input box if there is an error message pres-ent.

case "error": $("input[name='couponcode']").after("&nbsp;&nbsp;<span style='color: red; font-weight: bold'>" + this.data + "</span>") return; break;

Invalid CodeFigure 22.

Page 217: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax216

case "paypal_data": $("input[name='encrypted']").attr("value",this.data); return; break;

case "paypal_data": $("input[name='encrypted']").attr("value",this.data); $("#paypal_submit").unbind("click"); $("#paypal_submit").trigger("click"); return; break;

Let’s now work on the possibility that we have good data back. If we do, we’ll load our empty input with the data. Since all of this data will be hidden, there should be absolutely no change to the user interface.

Now after we’ve loaded this data, we’re ready to send the data on to PayPal. However, we’ve al-ready binded the click event, so clicking the but-ton again will do nothing except process another Ajax request. What’s one to do?

We can unbind the button, and manually initiate a click event. Let’s go ahead and do that.

That’s it! We’re done with our script file, at least for now. Let’s now move on to processing the Ajax request.

Page 218: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 217

Processing Our Ajax RequestLet’s begin by opening the ajax-processor.php file if you already haven’t.

First, we’re going to load the WordPress environ-ment using the wp-load method. Since we don’t need any plugins loaded, we’re going to define the WP_INSTALLING constant.

Now it’s time to work on some sanity tests for our two passed POST variables: id and coupon.

Since we’re likely going to be querying our data-base with this information, we need to make sure id is an integer and coupon is a string (with only alphanumeric characters).

A simple regular expression does the trick:

<?php header('Content-Type: text/html'); define('WP_INSTALLING', true); $root = dirname(dirname(dirname(dirname(__FILE__)))); if (file_exists($root.'/wp-load.php')) { // WP 2.6 require_once($root.'/wp-load.php'); } else { // Before 2.6 require_once($root.'/wp-config.php'); } ?>

Page 219: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax218

At this point, we would be querying our database with the $_POST[‘id’] variable, but there is no da-tabase except for the Holy Database in the sky.

Let’s create an associative array that will repre-sent what we might expect the database to return when we perform a query.

if (!isset($_POST)) die(''); if (!preg_match('/^[0-9]+$/', $_POST['id']) || (!empty($_POST['coupon']) && !preg_match('/^[0-9A-Za-z]+$/', $_POST['coupon']))) { die(''); }

The associative array $table will represent our da-tabase. The array $result will represent us retriev-ing the results from the database.

$table = array('100' => array( 'size' => 'small', 'price' => '10.00' ), '101' => array( 'size' => 'medium', 'price' => '15.00' ), '102' => array( 'size' => 'large', 'price' => '20.00' ) ); $result = $table[$_POST['id']];

Page 220: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 219

We can now set our $size and $price variables.

Up next is checking our coupon code. We’ll as-sign associative array $coupons with the available coupons, and $discount_percentage will hold the result.

$result = $table[$_POST['id']]; $size = $result['size']; $price = $result['price'];

If the coupon is not found in the $coupons array, we return the “Invalid Code” message to the user.

//Check coupons $coupon = strtoupper($_POST['coupon']); $coupons = array('BLOWOUT' => '75', 'BIGSALE' => '50'); $discount_percentage = '0'; if (!empty($coupon)) { if (!array_key_exists($coupon,$coupons)) { $response = new WP_Ajax_Response(); $response->add(array( 'what' => 'error', 'data' => 'Invalid Code' )); $response->send(); die(''); } else { $discount_percentage = $coupons[$coupon]; } }

Page 221: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax220

First, we have to do a sanity check and use the strtoupper function to make the coupon upper-case just in case the user entered it in lowercase.

If the coupon is empty, we do nothing.

We next check if the coupon exists in the $cou-pons array via the array_key_exists function. If it doesn’t, we spit out an error message.

If the coupon is found, we will assign the discount to the $discount_percentage variable.

Awesome. We now have all of our necessary vari-ables.

Now it’s time to work on sending our informa-tion on to PayPal.

This will require several steps, which I’ll go over in the next several pages:

Setting up a private and public certificate with •PayPal.

Setting up a connection to PayPal via OpenS-•SL.

Sending and receiving a response.•

Page 222: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 221

Setting Up a Private and Public Certificate With PayPalIf you’re brave enough to open the PayPal Web-sites Payments Standard Integratin Guide (PDF), venture to page 278.

Within this document, we are instructed to per-form four steps in order to interact with PayPal.

Create a private key.•

Create a public certificate.•

Upload the public certificate to PayPal.•

Retrieve PayPal’s public certificate.•

To create our private and public certificates, we need to use OpenSSL to generate them. The Pay-Pal documentation provides the OpenSSL code necessary to create your certificates.

However, I’ve found an easier way.

There’s a website called Stellar Web Solutions that allows us to generate certificates through a web-based interface.

Page 223: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax222

Follow the link above and let’s create some certifi-cates.

Once you click on “Create Certificate”, you are taken to a download page (with instructions).

Save the certificates to your computer for later use.

Let’s now upload our public certificate to PayPal.

Per PayPal’s instructions, here are the steps to up-load your public certificate:

Stellar Web Solution’s Certificate GeneratorFigure 23.

Page 224: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 223

Log in to PayPal and click on your “Profile” •subtab.

Click the “Encrypted Payment Settings” link •located in the “Selling Preferences” column.

Once the page has loaded, locate your “Public •Certificates” section and click “Add”.

Adding Your Public CertificateFigure 24.

You’re now taken to a new page where you can •browse your computer for the saved public certificate.

Page 225: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax224

Browsing for the Public CertificateFigure 25.

Certificate SuccessFigure 26.

Go ahead and click “Add” and you should be •presented with a “Success” message.

At this point, our public certificate should have been uploaded to PayPal.

Now, write down, save, store, whatever, the “Cert ID” you see for your uploaded certificate. You will need this later.

Page 226: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 225

Now it’s time to retrieve PayPal’s public certificate. You should still be on the page that says “Website Payment Certificates.”

There’s a “Download” button for PayPal’s public certificate. Let’s go ahead and save this certificate on your computer for later use.

You should now have the following stored on your computer:

Your private key.•

Your public certificate.•

PayPal’s public certificate.•

Your Certificate ID (Cert ID).•

Now this is where we’re going to have to part ways slightly in order to solve this PayPal dilemma. My web root is going to be different from yours, and my certificate ID is going to be different as well.

PayPal’s Public CertificateFigure 27.

Page 227: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax226

First, let’s move our certificates to a level below your web root.

Assuming your root is something like /home/user/public_html, let’s create a directory called certifi-cates.

The certificates directory should end up being lo-cated here: /home/user/certificates.

This ensures that nobody can take a peek at your certificates. Your certificates directory should now contain your public certificate, your private key, and PayPal’s public certificate.

We’re all set up now to move on to the next sec-tion, which will be setting up a connection to PayPal via OpenSSL.

Setting up a Connection to PayPal via OpenSSLThe first step in this section is to make sure your host supports OpenSSL. The easiest way is to check your phpinfo.php file.

If you can’t find this file, creating it is easy enough. Create a blank PHP file in your web root and insert this code:

Page 228: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 227

Now browse down to the OpenSSL section. If your host has it installed, the OpenSSL support parameter should be set to “enabled”.

If OpenSSL support is not enabled, contact your host and have them install it. Don’t let them trick you into buying expensive certificates. Just state that you need OpenSSL support for a script you’re running and refer them to your phpinfo.php file.

Now that we’ve verified that OpenSSL is indeed enabled, it’s time to get back to our ajax-proces-sor.php file.

Let’s create four variables:

$public_cert_id• - Will hold the ID PayPal gave us for our public certificate.

$public_cert_path• - Will hold the path to our public certificate.

<?php phpinfo() ?>

OpenSSLFigure 28.

Page 229: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax228

$private_key_path• - Will hold the path to our private key.

$paypal_cert_path• - Will hold the path to the public PayPal certificate.

I renamed my certificates to be a little more friendly on the eye, so my path variables may look different than yours.

//PayPal cert stuff $certificates_path = "/home/user/certificates/"; $public_cert_id = 'LGWWX2A9ZUARY'; //my key $public_cert_path = $certificates_path . "pubcert.pem"; $private_key_path = $certificates_path . "prvkey.pem"; $paypal_cert_path = $certificates_path . "paypal_cert_pem.txt"; if (!file_exists($public_cert_path) || !file_exists($private_key_path) || !file_exists($paypal_cert_path)) { die(''); }

The variables you will want to modify here are $certificates_path and $public_cert_id. After that, we should be on the same sheet of music.

Let’s now set a path to the OpenSSL binary. In my particular case, it is located at /usr/bin/opens-sl.

//OpenSSL $openSSL_path = "/usr/bin/openssl";

Page 230: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 229

Up next is building an array of variables we want to send to PayPal.

I’ll do my best to explain each one we use, but there are a bunch more in the HTML Variables for Website Payments Standard page.

Let’s start with a quick table of the variables we’re going to pass to PayPal.

After that, we’ll build the array in our Ajax proces-sor and send it on to PayPal.

Variable Value Description

business [email protected] Your PayPal e-mail address.

cmd _xclick _xclick is used for "Buy Now" buttons.

notify_url http://www.yourdomain.com/listener.php

PayPal sends information to this URL in the form of an IPN message. We'll define and cover this later.

amount TBD The amount of the t-shirt we are selling.

discount_rate TBDThe percentage we are giving off for a coupon code.

item_name "My Awesome t-shirt!" The name of the item.

Page 231: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax230

Variable Value Description

item_number The unique ID of the t-shirt size.

A pass through item that PayPal returns so you can adjust stock counts if needed.

quantity 1 Self explanatory.

return http://yourdomain.com/success/

A URL to a "Success" page after the user has completed checkout.

cancel_return http://www.yourdomain.com/error/

A URL to an "Error" page if the user cancels the checkout process.

bnPP-BuyNowBF:btn_buynowCC_LG.gif:NonHosted

A string identifiying the builder of the button.

cert_id TBD

This is the public certificate ID you received from PayPal. Mine is LGWWX2A9ZUARY.

For the notify_url variable, create an empty file called listener.php and place it in your web root. Make sure you set the permissions for the file to 755.

Let’s also create a WordPress page for both the re-turn and cancel_return variables (let’s use a page slug of success and error respectively).

Page 232: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 231

For the bn variable, we typically need to specify the builder (us) in this format: <Company>_<Service>_<Product>_<Country>

However, since we’re just using a “Buy Now” but-ton, we use the value: PP-BuyNowBF:btn_buynowCC_LG.gif:NonHosted.

The “NonHosted” portion just states we’re using a button that is not hosted with PayPal.

Here’s my finished array. You’ll want to modify the various keys to match your setup.

$paypal_args = array('cmd' => '_xclick', 'business' => '[email protected]', 'notify_url' => 'http://www.yourdomain.com/listener.php', 'amount' => $price, 'discount_rate' => $discount_percentage, 'item_name' => "My Awesome t-shirt - " . $size, 'item_number' => $_POST['id'], 'quantity' => 1, 'return' => 'http://www.yourdomain.com/success/', 'cancel_return' => 'http://www.yourdomain.com/error/', 'cert_id' => $public_cert_id, 'bn' => 'PP-BuyNowBF:btn_buynowCC_LG.gif:NonHosted' );

We then *ahem* borrow some code from this thread. I’ve of coursed modified it to suit our vari-ables and response.

Page 233: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax232

//Begin retrieving encrypted data $data = ""; foreach ($paypal_args as $key => $value) { if ($value != "") { $data .= "$key=$value\n"; } } $openssl_cmd = "($openSSL_path smime -sign -signer $public_cert_path -inkey $private_key_path " . "-outform der -nodetach -binary <<_EOF_\n$data\n_EOF_\n) | " ."$openSSL_path smime -encrypt -des3 -binary -outform pem $paypal_cert_path"; exec($openssl_cmd, $output, $error); if (!$error) { $output = implode("\n",$output); $response = new WP_Ajax_Response(); $response->add(array( 'what' => 'paypal_data', 'data' => $output ));

$response->send(); die(''); } else { return "ERROR: NR $error"; }

Don’t even begin to ask me how it works, but all I know is it just does. Now there are so many things that can go wrong here, so I refer you to this thread if the above code doesn’t work.

If you want things to get even more complex, here’s an entire class based on button encryption.

Page 234: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 233

Personally, I’d rather stick with the above code.

Now let’s perform some sanity checks since we should be finished.

If you’re debugging with Firebug, go ahead and set a breakpoint on the line where we assign the data to our hidden input box. If you expand the this object, you should see a bunch of red gibber-ish. This is our encrypted data.

Let’s go ahead and remove the breakpoint, reload the page, and then click on the “Buy Now” but-ton. If it goes through to PayPal with our price points, we can consider this a minor victory.

Gibberish HeavenFigure 29.

Page 235: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax234

Let’s also enter our two coupon codes (BLOW-OUT and BIGSALE) for each item. Here are the values you should expect:

If you have received those expected values, you can clap, rejoice, and scream wildly (just be mind-ful of those next to you).

We have now achieved our objective: we created a dynamic PayPal Buy Now button with coupon capabilities. Congrats!

One last point before I conclude this section: when the user completes the payment process, they should be taken to your “Success” page.

If a user cancels, they should be taken to your “Er-ror” page (hopefully laden with expletives).

It’s up to you to fill out those pages.

Item (T-Shirt)

Price Coupon BLOW-OUT (75%)

Coupon BIGSALE (50%)

small $10.00 $2.50 $5.00

medium $15.00 $3.75 $7.50

large $20.00 $5.00 $10.00

Page 236: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 235

In the next section, I’ll go over Instant Payment Notifications, which allows you to do something with all the PayPal data of the current transac-tion.

Setting up a Simple Instant Payment Notifica-tion (IPN) ScriptPayPal’s Instant Payment Notifications (IPN) al-low you to set up a PHP processor page and access all of the various variables PayPal sends via IPN. This processor page is called a listener.

From this listener, you can parse the data and per-form any actions necessary (update inventory, as-sign user priveleges, etc...).

In the last section, we created an empty PHP file called listener.php and placed it at your web root.

Let’s fill it in with some code. For starters, let’s load the WordPress environment without the need of plugins (I’m assuming your wp-load.php is at your site root. If not, you will need to adjust the require_once).

Page 237: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax236

Up next is pointing to the correct PayPal URL. In order to verify the IPN transaction with PayPal, we need to send all of the variables back to Pay-Pal.

If we’re working on a production install, the URL will point to PayPal’s main site. When testing, however, the URL will point to the PayPal sand-box.

<?php define('WP_INSTALLING', true); require_once('../wp-load.php');

Now we send the information back to PayPal for verification.

if ($_POST['test_ipn'] == '1') { $paypalurl = 'sandbox.paypal.com'; } else { $paypalurl = 'www.paypal.com'; }

// read the post from PayPal system and add 'cmd' $req = 'cmd=_notify-validate'; foreach ($_POST as $key => $value) { $value = urlencode(stripslashes($value)); $req .= "&$key=$value"; } // post back to PayPal system to validate

Page 238: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 237

After that, we set up some e-mail variables so that the IPN messages can be e-mailed to us.

$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: " . strlen($req) . "\r\n\r\n"; $fp = fsockopen ($paypalurl, 80, $errno, $errstr, 30);

//Set up email alerts $to = "[email protected]"; $subject = "IPN Messages"; $message = ''; foreach ($_POST as $key => $value) { $message .= $key . " = " .$value ."\n\n"; } $site_name = str_replace('"', "'", get_bloginfo('name')); $charset = get_settings('blog_charset'); $headers = "From: \"{$site_name}\" <{$to}>\n"; $headers .= "MIME-Version: 1.0\n"; $headers .= "Content-Type: text/plain; charset=\"{$charset}\"\n";

We now check to see if the IPN has been verified and we e-mail the results to ourselves.

if (!$fp) { // HTTP ERROR } else { fputs ($fp, $header . $req); while (!feof($fp)) { $res = fgets ($fp, 1024);

Page 239: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax238

We’re done! Remember, set the listener.php file to 755.

Now it’s time to go play in the sandbox. The Pay-Pal Sandbox to be exact.

If you don’t have a PayPal Developer account, head on over and create one.

Once you’re logged in, go down to “Test Tools” and click on IPN Simulator.

if (strcmp ($res, "VERIFIED") == 0) { //Do your custom logic here //Send email with IPN vars @wp_mail($to, $subject, $message, $headers); } else if (strcmp ($res, "INVALID") == 0) { //error @wp_mail($to, $subject, $message, $headers); } } //end while fclose ($fp); } ?>

IPN SimulatorFigure 30.

Page 240: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 239

From there, enter your IPN Handler URL (the lo-cation of your listener.php) and set the Transac-tion Type to Web Accept.

IPN Handler and Transaction TypeFigure 31.

Now scroll all the way to the bottom and click on the “Send IPN” button.

Send IPN ButtonFigure 32.

If everything has been set up correctly, you should see a “Success” message.

IPN Success MessageFigure 33.

Page 241: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax240

Now go check your e-mail. You should have re-ceived an e-mail similar to the figure below.

E-mailed IPN MessagesFigure 34.

Now you have a good idea of what variables you’ll be receiving via your listener.php file.

Page 242: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 14, Example #3: Dynamic PayPal Buy Now Options With Coupon Codes 241

You can then use the listener to update the data-base, activate user accounts, automate shipment, and much more. What you do with all the data is up to you.

Up next is testing via production, but I'll leave that one up to you.

Example ConclusionIn this example, we covered the following:

Setting up a PayPal Buy Now user interface •with a field for a coupon code.

Capturing the user interface variables and •passing them to the Ajax processor.

Processing the data via the Ajax processor and •retrieving encrypted code from PayPal.

Presenting this encrypted code to the front-•end and submitting the Buy Now request.

Implementing an IPN listener to capture all •the data.

You should now have a basic foundation for build-ing your own PayPal scripts.

Page 243: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner
Page 244: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 15

Reduce Your JavaScript Footprint

Page 245: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax244

Reduce Your JavaScript FootprintIn this chapter I’m going to discuss how to reduce the loading time of your JavaScript files.

The first section will cover a plugin called W3 Total Cache. The second section will cover script com-pression.

W3 Total CacheIf you’re looking to reduce page loading time and basic script compression, I highly recommend W3 Total Cache.

If you have a lot of JavaScript localized variables, you will notice them all on a new line. I’m not sure why WordPress does this, but W3 Total Cache will rectify this by compressing the page code as much as possible.

After you’ve installed the plugin, head to the “Minify” sub-heading.

Find the “JS Minify Settings” and check the fol-lowing options:

Minify•

Comment Removal•

Page 246: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 15, Reduce Your JavaScript Footprint 245

Line Break Removal•

You can take this even further by checking all of the options on the “HTML Settings”.

JS Minify SettingsFigure 35.

Once both the HTML and JS minify settings are set, take a look at your site’s page source. It should look like a lot of compressed gibberish.

HTML Minify SettingsFigure 36.

Page 247: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax246

And that’s it for W3 Total Cache. Now let’s work on compressing our script files.

Script CompressionScript compression has gotten a lot easier thanks to a website called The JavaScript Compressor-Rater.

If you head on over to the site, you’ll see a big text box at the bottom with several checkboxes with compress options.

In our particular case, we’re only interested in YUI Compressor and Packer. Also be sure to uncheck the GZIP option, since not all browsers currently support GZIP’ed scripts.

Compressed Page OutputFigure 37.

Page 248: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 15, Reduce Your JavaScript Footprint 247

For starters, find one of the scripts we played with during this book. Copy the text within the script file and paste it into the CompressorRater text box.

Click on the “CompressorRate It!” button. Once it’s compressed, you’ll be presented with a screen with a table of the various compressed options. Clicking “View” on each one will display the compressed code.

CompressorRater OptionsFigure 38.

List of Compressed ScriptsFigure 39.

Page 249: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

WordPress and Ajax248

Find the row for “YUI Compressor 2.4.2” that has no options. Click “View”. This particular view is important since the YUI Compressor will properly format our script for some serious com-pression.

A pop-up should come up. Select all the text and copy it.

Back on the CompressorRater site, find the text box and paste the new code in. Click the “Com-pressorRate It!” button again.

You’re presented with a similar list of compressed options. Now we’re after the row with “Packer 3.1” with option “base62”. The “base62” option is as close as we’ll get to GZIP’ed scripts (make sure you select from the first table only).

When you click on “View”, you should see some highly compressed JavaScript code. Select and copy the text, paste it into a new file, and save it.

In the case of the code shown below, I used the paypal_script.js file that we worked on in Exam-ple #3. The compression took it from 4KBs to a little over 1KB. For larger scripts, you’ll see an even bigger difference.

Page 250: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

Chapter 15, Reduce Your JavaScript Footprint 249

ConclusionIn this chapter you learned two ways to reduce your JavaScript footprint.

And now you’re at the end of the book (awww, so sad *tear*).

Well, thank you for putting up with me through-out this journey. My hope is that you have a much better understanding of WordPress and Ajax than you had before.

Take care and God bless.

eval(function(p,a,c,k,e,r){e=function(c){return c.toString(36)};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[0-9g-s]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('j(document).ready(1(){2 a=j;a.k={l:1(){a("#5").bind("6",1(){2 c=a("select[3=\'sizes\'] option:selected").7("8");2 b=a.trim(a("9[3=\'m\']").7("8"));2 d={};d.n=a(0);d.g="h-g";d.type="POST";d.url=paypalscriptvars.ThemeURL+"/h-processor.php";d.4=a.extend(d.4,{coupon:b,id:c});d.global=o;d.timeout=30000;d.success=1(f){2 e=wpAjax.parseAjaxResponse(f,0.g,0.n);a.each(e.responses,1(){switch(0.what){p"error":a("9[3=\'m\']").after("&q;&q;<r style=\'color: red; font-weight: bold\'>"+0.4+"</r>");i;s;p"paypal_data":a("9[3=\'encrypted\']").7("8",0.4);a("#5").unbind("6");a("#5").trigger("6");i;s}})};a.h(d);i o})}};a.k.l()});',[],29,'this|function|var|name|data|paypal_submit|click|attr|value|input|||||||response|ajax|return|jQuery|paypalscript|init|couponcode|element|false|case|nbsp|span|break'.split('|'),0,{}))

Page 251: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner
Page 252: WordPress and Ajax · First things first... What the heck is Ajax anyways besides a lovely acronym (Asynchronous JavaScript and XML)? It’s definitely not a common house-hold cleaner

“This book answered all of my Ajax questions and some I didn’t even know I had!”

— RYAN HELLYER pixopoint.com

“I’m impressed. This book is really comprehen-sive. Language seems very clear and code sam-ples seem helpful.” — DARREN HOYT darrenhoyt.com

“After reading quite a few random tutorials on-line, I still had ‘no idea’ what Ajax was all about and how I could make use of it together with WordPress. After reading this book however, I can build some pretty advanced functionality. And not by copy/pasting examples, but with actual understanding of how and why things work.” — ØYVIND ENGER morsom.no

RONALD HUERECA (ronalfy.com) has worked with WordPress since 2006 and has released several WordPress plugins, his most popular being Ajax Edit Comments (ajaxeditcomments.com). His education includes a Master’s in Business Administra-tion and a degree in Electronics Engineering Technology.

WORDPRESS & AJAX — wpajax.comAn in-depth guide on using Ajax with WordPress