Get it All
Together

As much as I love WordPress, I am continually frustrated with the pace at which forms get created. That’s because, while WordPress seems to have thought of everything else, the most basic part of creating interactive websites seems to have been ignored: the forms.

Yes. There are front end form builders like Ninja Forms. But while they all work well within their own context, much of the customization of the forms either requires additional, paid-for plugins or simply isn’t possible due to constraints of the plugins themselves. Besides which: I’m not often building front-end forms, but rather forms to add metadata to posts or set configuration variables for the site.

What I have wanted and needed was something like CakePHP’s FormHelper: an interface that simplifies the process of outputting HTML5-valid forms and inputs. Something that allows me to specify the settings for an input, and puts the aggravating process of laying out the form in it’s own hands. And so I created EasyInputs: A Forms generator for WordPress.

The plugin is still in early stages of development. But so far, I’ve been able to clean up and cut down on code for one of my other projects significantly. And besides speed and accuracy, there are a couple of key advantages to building forms in this way. For a start, if you’re trying to keep your WP code as close as possible to PSR2 standards, adding values to an array is a lot more convenient than sprintf’ing the long HTML code associated with forms. Secondly, the code output from my plugin is significantly clearer than a rat’s nest of escaped and unescaped HTML code.

WP-Scholar’s Person CPT with escaped HTML:

WP-Scholar’s Person CPT with EasyInputs:

This is just a relatively basic example. The plan is to support every HTML-valid input including <datalist>, <keygen> and <output>. Right now, these new elements are missing. I’ll be updating that shortly (perhaps before you even read this!), along with some basic HTML security: every EasyInputs text input will include the maxlength attribute.

I know that the WordPress devs are working on some sort of fields API. But as I’ve observed it, the development process still banks too heavily on bringing those fields into the WP regime, which may satisfy the needs of the WordPress team, seems too rigidly focused. Developers don’t need a complete solution for every little thing, they need simple tools that do simple things well. That’s the aim of this development project.

Please have a look at the latest stable tag and tell me what you think!

So the issue is this: I’m creating a faceted search interface for a client’s application. Rather than clutter a window with textbox after textbox, select after select, most of which they don’t use on a regular basis, my solution is to create dynamic form fields based on the user’s requests. Need to search a SKU? No problem! Need to select from a list of clients? Gotcha covered.

All of this has worked out quite swimmingly overall, but a bug was brought to my attention today that I had to share with the rest of the Internet. Specifically: users were typing their responses into a jQuery Autocomplete field and then clicking “Submit,” only to find that their response was not recorded and not passed on in the query. Annoying!!

With a bit of digging, I discovered the problem. The jQuery autocomplete UI does not allow the value of the field to be updated until one of two events is fired: “change” and “select.” Trouble is: if you go directly from typing a value into the autocomplete field, directly to the submit button, the “change” event does not have time to fire.

The solution to the problem, which I discovered on the always-helpful StackOverflow website, follows below. The gist of it is that you need to first prevent the form from submitting, double-check that autocomplete fields have been properly “change”-d and then submit the form:

I’m sure I’m not alone among developers who are continually frustrated by the lack of imagination shown for implementing the Customizer in WordPress. Sure, I have praised its existence and support its use to replace the labyrinthine system of developer-created admin pages. But then once I got into serious development work, I quickly found that having all the HTML controls handed to you means having the ability to decide how they get rendered taken away from you.

For example, I’d like to give users of my core theme the option to define the number and content of columns on their site. Doing so obviously means creating repeating fields: first, you define the user-specified number of columns, then you produce enough identical fields as necessary to define each column. In my case, that meant providing an ID, column width at various responsive sizes and of course, the content of the column.

Yes. I could certainly decide that no one needs more than four columns and just assign each column it’s own static block of controls. If there’s no Column 4, then those settings are just ignored. But this is a sloppy way to build a UI that is going to naturally lead to some confusion when changing settings never seem to have any effect. Better, then to just have dynamically created settings blocks. One drawback: the Customizer API has no repeating fields and doesn’t even include a vehicle for a button that doesn’t submit, leaving you without the option to create a JS-driven solution.

After months of frustration, I eventually found Kirki. Now, we are talking! I currently include Kirki as a Composer package into the core of my base theme, but it’s also available as a plugin for those who would be more comfortable with that solution. From the Kirki homepage:

Kirki is a Toolkit allowing WordPress developers to use the Customizer and take advantage of its advanced features and flexibility by abstracting the code and making it easier for everyone to create beautiful and meaningful user experiences.

So, it’s not an entirely new API or a replacement. It simply harnesses the power already manifest in the Customizer, but gives it a few gooses that make it a whole heck of a lot easier and more flexible to use.

Using Kirki

Kirki frees you to create forms that properly express what your theme does, rather than having to build all that functionality into your theme or worse than this, simply allow your theme to be driven by the limitations of the current Customizer UI. A full spectrum of HTML5 form elements is introduced, along with a plethora of more exotic controls. Especially useful for me – beside the repeater control – were the multicheck and radio-image controls. But there’s so many more that I’d like to play around with! Maybe for some custom implementations, I suppose.

Replacing the existing Customizer API calls with the Kirki API calls was a snap in fact, about the only confusion was that there was so much less code than before, I had to keep checking that I’d actually gotten all the controls passed over:

 // Icon Fonts:
Kirki::add_field( 'hn_reactive', array(
'type' => 'multicheck',
'settings' => 'icon_fonts',
'label' => esc_attr__( 'WeLoveIconFonts', 'hn_reactive' ),
'section' => 'icon_fonts',
'priority' => 10,
'choices' => ReactiveCustomizer::$icon_fonts,
) );

I use nine fonts from WeLoveIconFonts.com, so as you might imagine, a huge selection of code simply disappeared when this Kirki version replaced it. Who doesn’t love that??

As for building repeatable blocks of controls, this too is a simple matter:

/* 
// Columns:
*/
Kirki::add_field( 'hn_reactive', array(
	'type'        => 'repeater',
	'label'       => esc_attr__( 'Column Definitions', 'hn_reactive' ),
	'section'     => 'hn_layout_defaults',
	'priority'    => 9,
	'settings'    => 'column_contents',
	'default'     => array(
		array(
			'content' 	=> 'post_data',
			'small'		=> '9'
		),
		array(
			'content'	=> 'default',
			'small'		=> '3'
		),
	),
	'row_label'		=> array(
		'type'		=> 'text',
		'value'		=> 'Column'
	),
	'fields' => array(
		'col_id' => array(
			'type'			=> 'text',
			'label'			=> esc_attr__( 'CSS Column ID', 'my_textdomain' ),
			'description'	=> esc_attr__( 'Any XHTML-compatible ID you like', 'my_textdomain' )
		),
		'content' => array(
			'type'			=> 'select',
			'label'			=> esc_attr__( 'Content', 'my_textdomain' ),
			'description'	=> esc_attr__( 'Specify what kind of content will go in this column', 'my_textdomain' ),
			'choices'		=> $this->content_choices(),
		),
		'small' => array(
			'type'			=> 'text',
			'label'			=> esc_attr__( 'Cols at small', 'my_textdomain' ),
			'description'	=> esc_attr__( 'How many grid columns this column takes up at small and up sizes.', 'my_textdomain' ),
		),
		'medium' => array(
			'type'			=> 'text',
			'label'			=> esc_attr__( 'Cols at medium', 'my_textdomain' ),
			'description'	=> esc_attr__( 'How many grid columns this column takes up at medium and up sizes.', 'my_textdomain' ),
		),
		'large' => array(
			'type'			=> 'text',
			'label'			=> esc_attr__( 'Cols at large', 'my_textdomain' ),
			'description'	=> esc_attr__( 'How many grid columns this column takes up at large and up sizes.', 'my_textdomain' ),
		)
	)
) );

I have so far found Kirki to be exactly the tool that I need, until of course WordPress comes up with their own solution.. and who knows when that will be?

Took me basically all day to get this done, but as of this moment, HolisticNetworking.net is now powered by the newly-fused WordPress 3.0 installation. Whereas previous versions of WordPress were branched into WPMU and WordPress classic versions, the new version brings it all back together (very Holistic, actually!) into a single installation.

I’m going to confess that vanity made me resist this new version: WPMU was the advanced version of WordPress that made me feel cool. But given the way in which I generally use WordPress MU on this site, the new synthesized version of WordPress is probably a better choice anyway.

On a technical basis, one of the things that concerned me most was how the association between the various blogs would differ with this new installation and whether carefully-built plugins that deliver the front-page content would end up broken as a result of the upgrade. I’m not sure whether the new version introduces new methods of access, but certainly the old ways have been maintained and the transition has been fairly straight-forward as we’ve come to expect from the great folks at Automattic.

So the question now is: how does this affect my WordPress MU Function List? Are these functions still relevant and should I create an entirely different page for new stuff? I must confess that I’ve been out of the WordPress game and only just barely paying attention to the Hackers list, so perhaps tomorrow is a good day to correct that laziness on the WP front!

Another task for the near future is yet another redesign of the front page. My “Fluidity” experiment was a good one and worthwhile. But now that I’ve had a chance to see what the front page looks like on a high-resolution wide-screen monitor, it’s clear that the experiment should be brought to its swift conclusion. My blue and white pipes theme will doubtless be maintained, but I’d like to return to a fixed-width design. All in good time!!

It will doubtless seem over-obvious to those veterans out there, but for those who are diving into CakePHP for the first time, let a fellow first-timer let you in on the first, most important secret of proper CakePHP building: nothing matters until you have your database.

“Sure,” you say. “Of course I know I need a database to build a project from.” No. I’m not saying you need one. I’m saying that until you’ve got your database designed, built, tested, benchmarked and completely sound, there’s really no point in building the CakePHP aspect of it. This may not be of monumental importance to those doing simple things, such as building a rudimentary blog or content management system (why aren’t you using WordPress or Drupal?). But since the real purpose of a framework like CakePHP is to develop applications other than those already provided an Open Source solution, the majority of developers do need to consider this all-important fact before they start hacking away at some high-value project or another.

Really, its at the moment when you begin to think about developing with Cake that you take your first important step towards being some kind of database administrator. CakePHP will only provide the presentation layer for your application, along with some basic calculations and such. The real meat of most applications will happen inside the database, or at least, the efficiency with which you design your database will determine the ability to gather the data you’ve previously stored and therefore determine the efficiency of your site to its users.

In my opinion, this is especially true because CakePHP’s methods for dealing with data tend to be something on the less-efficient side of the curve. Regardless of your opinion on the efficiency of CakePHP’s models, there is no question that those models are meant to reflect – not influence! – your database design.  As your understanding of your database increases, your understanding of how best to form the models will also increase at the same rate. Once you can easily extract the right data in the right order directly from MySQL (or phpMyAdmin, if you like), you’re well on your way towards creating efficient, practical code in your models and controllers to deal with same.

Thus it pays to spend a lot of time carefully planning out your database and normalizing it to the best of your abilities. When considering table structures, give a bit of thought to the following:

  1. What are the broad-strokes, raw data types you will be using? Orders? SKUs? Ratings? Users? This helps determine what the tables aught to be.
  2. What are the attributes of each data type that you need to capture? Think deeply on this one. Is the attribute in question really associated to the data type, or is there a better data type you need? Is there a new table to add in/modify?
  3. Math matters! Performing mathematical formulas on data with MySQL is more efficient in many cases than drawing the data out and then performing math, but it means that query results cannot be cached. Which method works best for your database and application depends greatly on your circumstances. Plan and test this aspect of your system very carefully.
  4. Math matters, part the second: if performing mathematical queries seems the best option for certain functions of your system, consider isolating those attributes of a given data type in their own table. Basically, you might get a performance boost from dividing a subject into two tables, one containing the non-math attributes and the other containing the math attributes.
  5. Have you considered cache tables? Even those things, like COUNT(*), which don’t seem like math, strictly speaking, take efficiency away from your database. Just plain old queries take time. Perhaps those tables that get hit hard with queries should have summary cache tables to support them? A multi-table, multi-join query might for example be run every ten minutes, with the data and time of update saved to a cache table for every query in between.
  6. Indexes! Its the ever-present challenge of database design: having enough indexes for enough query types, but not too many. A good tool for this is to just start writing down the anticipated queries you think you’ll be running on the data. What fields make sense to index in this case? Can you use values like timestamps to serve as primary keys instead of single-use IDs? Can you combine fields into a single index and have that be a relevant index for a sufficient number of queries?

And planning is only one part of the process of getting a nice database on which to build an application. The next step is to fill it full of data (even just garbage data, as long as it conforms to what you’d expect in your application) and start running queries against the tables. How well do they perform with a gigabyte or so of data to sift through? Can you knock together a few new indexes and make it perform better?

Of course, as an application evolves, you may well find that the design you started with is not adequate over time. This happens. But starting with something tuned precisely to your needs will get you a lot farther once you get to the CakePHP side of things, either way.

If you’re like me, you’ve got tons of social networking sites to keep track of. Personally, I think I was born without the self-control that prevents me from signing up with the next, latest-and-greatest social network just to see what it does. Keeping track of all that mess – and keeping it updated – is almost a full time job by itself.

And yet I’m committed to using social networking sites as a means to establish my authority on the web. I’ve been actively working to get the maximum bang for my buck with social networking, spreading my posts as far and as wide as I can manage. On the web design side, I have accounts on LinkedIn, Twitter, Delicious, Mashable and WordPress.com. On the political side, I have accounts on FaceBook, MySpace, Twitter, YouAre, Flickr, Delicious and Tumblr. That’s in addition to one website for each side of my online identity.

To manage all these accounts, I’m using a social middleware service called Ping.fm. Ping.fm taps into the APIs of all these services – and loads more that I don’t personally use – to update my status whenever I update it on Ping.fm. It also allows me to set specific hashtags to trigger only a specific subset of my registered services. I have a Google Gadget on my Google home page to update Ping.fm from any convenient location. On top of that, I have a plugin for WordPress, on which my site is built, that will update Ping.fm every time I post a new article.

This system is working well for me so far, but I’m finding that new middleware services are opening up all the time. Just today, I discovered Posterous via an article on Mashable. This interesting service allows you to create your own subdomain on their site and post simply by sending an email to post@posterous.com or post@yoursub.posterous.com. This posting allows you to upload video, pictures and audio and have them automatically show up on the site. Then, you can also update all kinds of other social networking services as you go.

The other interesting advantage is that this service allows you to potentially create an entirely new ad-hock social network by allowing others to post to your Posterous. You can create as many Posterous sites as you like, those sites can update whatever other services you would like. The possibility is that people can create entire online communities that stretch over several services and for which they own no Internet real-estate.

There is another service for which I signed up, but which I have not had the pleasure of trying out, ZooLoo.com. This service seems to provide much of the same type of service as the other two. When I get more into that, I’ll report back.

But now, I’ve got a new problem, rather a lot like the first one: I have so many middleware sites that it’s just as confusing to use as the update services I was trying to organize! Sheesh, the cutting edge is complicated…..

It’s one of those bad habits we find hard to break as WordPress enthusiasts: installing plugins and then forgetting about them. This is especially true when you’ve installed them through the WordPress admin panels and then deactivated them because you decided you didn’t like them.

Well, now I’m getting very close to done with the new theme for DFE and I’ve got to go through all these plugins to find out what I’m keeping and what I’m not. You have to be very careful that the plugins you’re using do not conflict with one another or with template functions, and this new template is also going to be introducing a number of new functions and plugins both.

Because I’ve found that it just makes more sense to keep some things as template functions. I’ve not really bothered to play around with template functions because I’ve always seen the template as a display-only set of code. Indeed it is, but if there is additional processing to be done on display that isn’t needed anywhere else, WordPress provides the function file to deal with this. It’s not entirely MVC, but then, what is? Perfect systems don’t always work in an imperfect world.

Like eliminating unused plugins, moving display-altering functions to the template is about making things more efficient. If WordPress forces your server to spend time it doesn’t need to reading plugin files you’re not using, you take a hit on your performance. If you have functions which only operate on the display level, but which are contained in plugins, then each time any admin page is run, the plugin has to be turned on despite the fact that it will never be used. We’d like to avoid this, if at all possible.

On the other hand, adding huge volumes of code to your functions.php file has a similar effect: every time the front-end of your website is launched, you bog your system down with code. So, you need to balance what you use in the functions.php file and what you use as a plugin very carefully and make sure you’re using the most efficient code possible.

I just thought I’d write a note about the new Favorite Actions bar in the new version of WordPress. This seems to have been designed as a way to mollify the concerns of the people who used to like the old layout of the administration areas better. But it’s a handy little shortcut to important things and of course, there’s a natural tendency as a plugin developer to want to utilize this space for your own plugin.

In fact, I was looking up how to do this very thing when I happened upon this page. The author cautions, quite rightly, that using this shortcut bar too much will end up clogging the thing and making it useless.

So, it seems to me that the responsible solution for plugin developers who want to make use of this new space would be to make it an option of your plugin which is always disabled by default. Doing so would allow your plugin users to make their own choices about which options to include in the favourites and which to leave as standard options. It is, after all, called a “favorite” actions bar.

In fact, I am hoping that future iterations of the WordPress core will include options to make this bar genuinely customizable according to your own preferences. The combination of a customizable favorites list and optional inclusion of plugin functions would represent the best of all possible worlds for this easily abused space.

It recently became necessary for me to work with the WP-Cron API for a local band website. Those of you who’ve done any work with WP-Cron have no doubt noticed that the way cron jobs are held in the database can be a bit cumbersome to work with.

By this I mean: since the key under which all the arguments is held is a hash of all the arguments, they can be a bit difficult to get at when trying to reveal them or work with them in any way other than the direct API. I’m sure someone more familiar with the inner workings of the API could provide a decent explanation of why this is so, but I certainly cannot.

So in working with crons – I needed a way to be able to quickly visualize the crons setup by my plugin, which emailed a post like a mailing list, to delete any jobs sent in error – I had to come up with some functions that properly dealt with the WP-Cron array. I submit these functions to you now in hopes that this example might help someone else down the line:

Get the Schedule:


function bhd_get_schedule() {
$crons = _get_cron_array();
if ( empty($crons) )
return false;
foreach ( $crons as $timestamp => $cron ) {

if ( isset( $cron['bhd_cron_send_hook'] ) ) {
foreach($cron['bhd_cron_send_hook'] as $md => $stuff) {
$result[$timestamp] = $stuff['args'];
}
}
}
return $result;
}

Notice that this is really just a modification of the get_schedule() function already a part of the wp-cron API. However, what I’ve done in this case is simply provide a convenient way to bypass the hash and return a list of crons and their arguments, so they can be worked with conveniently. In my case, I only wanted one particular cron hook. If you have more than one, you might make the function take an argument that it then supplies to the isset line.

Displaying and Deleting Jobs


function bhd_delete_jobs($form) {
$jobs = bhd_get_schedule();
$deleted = 0;
foreach($form['cron_job'] as $input=>$timestamp) {
if ( wp_unschedule_event($timestamp, 'bhd_cron_send_hook', $jobs[$timestamp]) ) {
$deleted++;
}
}
return($deleted);
}


<?php if($jobs = bhd_get_schedule()) { ?>

Currently Scheduled Deliveries

Below is a list of the currently scheduled mailing list deliveries, meaning each represents an email with a batch of fifty mailing list members as recipients.

<input type="hidden" value="96f539126e" name="_wpnonce" id="_wpnonce"/>
<input type="hidden" value="/testing/wp-admin/options-general.php?page=bhd_mailinglist.php" name="_wp_http_referer"/>

<?php foreach($jobs as $job=>$args) { ?>

<?php } ?>

<?php echo(date('l jS of F Y h:i:s A', $job) ); ?gt; <?php print($args['subject']); ?> <input type="checkbox" value="<?php echo($job); ?>" name="cron_job[]" />
<input type="hidden" id="bhd_ml_cron-submit" name="bhd_ml_cron-submit" value="1" />
<input type="submit" value="Delete Jobs" />

<?php } ?>

I’ve been struggling to understand exactly how the cron functions of WordPress work.

I’ve been working on a plugin for some friends of mine that need a mailing list manager, this plugin in turn requires the ability to schedule tasks. So, I spent a long time puzzling over the various bits of documentation and posts on other blogs about WP-Cron, pouring over code and experimenting with my own, until I finally got a working plugin.

And while I’m sure that the information that is out there now about Cron functions is adequate for some, it’s clear to me that others may need a different set of explanations, which is where this blog post comes in. I’m going to attempt to lay out the WP-Cron world in a different way in hopes of improving slightly on what’s out there.

Let me start by saying that the best article I’ve read on the subject of crons is definitely “Timing is everything: scheduling in WordPress,” by Glenn Slaven. I highly recommend that, even before reading this article, you take a gander at that one. There are really just a few things on which I would like to expand and revise slightly, because they threw me off. I’m also slightly reorganizing the order of discussion into something more akin to a step-by-step set of instructions.

Editor Update: Ack! Looks like this other article is no longer available. Sorry, everybody!

The Scenario

The example we’re going to work with is the mailing list manager plugin I’m developing. We’re not going to get into too much of the actual code of that plugin, but the scenario is doubtless a common need. We’ll be assuming that we’re working with a blog which has many, many users. (real world example: 400+) We want to keep our users up to date with periodic email blasts. Rather than those blasts being separated from the regular content, we want to specify that certain posts will become both posts on the site and email messages.

But of course, sending emails out to 400+ recipients at once is going to make the SMTP server very, very suspicious. In order to avoid problems with the mail server, we need to throttle the messages by sending blasts every twenty minutes ((or at least, approximately twenty minutes, remember that WordPress cannot be exact about timing)) until the last user group has been emailed. To do this, we will be using the wp_schedule_single_event() function within the WP-Cron suite of tools.

Scheduling: What You’ll Need

In order to schedule an event of any kind, you’re going to need a few basic components. We’ll get into more detail further down the line, but it’s important to be able to identify the landmarks as we proceed:

  • Your Cron Function ~ This is the actual function that does the stuff you want when the scheduled event is fired
  • A Custom Action Hook ~ Here’s where we get into the nitty-gritty of how WordPress operates. It’s very simple, actually, but understanding how to create custom action hooks is key to understanding WordPress cron functions. And a great deal more about WordPress!!
  • Your Scheduling Function ~ This is the function that initially assigns the scheduled task.

Your Cron Function

my_scheduled_function([$arg1, [$arg2, [$arg3. . . ]]])

This function lies at the heart of your scheduling world! It is here that you put all the stuff you want to get done at a specified time. In our example, this is where you actually send the email to your latest batch of users. Note that this function can include an arbitrary number of arguments. We’ll get into how you pass those arguments on to the function later. For now, just notice that this is a standard function like pretty much any other. In fact, in the case of the mailing list manager I created, it’s as simple a function as you could ask for:

function bhd_cron_send($sender_email, $subject, $mail_text, $send_headers) {
	wp_mail($sender_email, $subject, $mail_text, $send_headers);
}

It’s probably advisable to create your function and make sure it works without adding in the cron functionality first. This is because a) there’s no need adding extra complexity to the system until you’re sure the function works and b) you’ll need to know how many arguments will need to be passed to the function and in what order.

Your Custom Action Hook

This is what really threw me. I’ve never created my own custom action hooks and never really understood them, but you’ll need to in order to get things done with cron, so this is important. Creating a custom hook is as simple as typing the following:

add_action('my_custom_hook', 'some_function' [, $priority, [$num_args]]);

WordPress assumes that anything added as the first argument exists without any additional registration of that new hook. In other words, ‘my_custom_hook’ actually is the declaration of the action hook in this example. The second argument should be a valid function name and will be the function associated with ‘my_custom_hook’ whenever ‘my_custom_hook’ is invoked anywhere in the system.

Obviously, since you’re writing your custom action, you’ll also need to invoke that custom action somewhere. That’s the last piece of the puzzle, which is explained down the page a bit.

But before we leave this topic, it’s worth pointing out that, just like any other action, it’s entirely possible to hook more than one function into the custom action you’ve created. It’s also possible to invoke the hook somewhere else in your code, separate from your Cron function. This bit of WordPress theory may prove helpful to you down the line, so it’s just an FYI.

The last two arguments of the add_action function are important. Recall the number of arguments you needed for your scheduling function. That’s the number you’re going to want to put in the $num_args add_action parameter. Otherwise, add_action doesn’t look for and doesn’t accept any arguments provided, which will doubtless render your function either inoperable or ineffective.

Late Update: Per jeremyclark in the comments, this is not really the reason to mess with priority! Thanks Jeremy! If you’re function is particularly computationally expensive (if it’s complex and relies on a lot of processing), it might be worth it to back the priority down a bit. You can do this by setting the $priority argument. The default is 10. In our case, the actual function is trivial, so this is what the add_action looks like for our mailing list:

add_action( 'bhd_cron_send_hook', 'bhd_cron_send', 10, 4 );

Your Scheduling Function

Finally, there is the function which will do the scheduling. Depending on the complexity of your plugin, this may not be very complex at all. But the one thing that is required of your function is that it bundle up all the arguments to your cron function into an associative array in the proper order. In our example, the parameters for our sending function are $sender_email, $subject, $mail_text, $send_headers. Thus, we create an array like so:

$mail_bundle = array(
     'sender_email' =&amp;gt; $sender_email,
     'subject' =&amp;gt; $subject,
     'mail_text' =&amp;gt; $mailtext,
     'send_headers' =&amp;gt; $send_headers
);

Next, we need to assign the scheduled task. For this, we use our custom action hook. Now we understand why it was so important to pay close attention to the number of parameters and their order: the hook only allows you to set the number of arguments and the function – which is now removed by one degree of separation from the schedule – needs those arguments sent to it in the proper order. We call the WordPress scheduling function like so:

wp_schedule_single_event(time()+$time, 'bhd_cron_send_hook', $mail_bundle);

The first argument is simply the Unix system time plus an interval of seconds after which we would like the scheduled task to be fired. Since we want the first of our batch email events to happen twenty or so minutes after the post has been published, $time was defined as follows:

$time = rand(1000, 1200);

“Wait,” you say, “why the random number?”

If you look at the multi-dimensional array of scheduled events created by the WP-Cron system, you will find that the primary key for identifying events is the Unix timestamp. The second-level identifier is an MD5 of the arguments. In our example, it’s possible that someone could inadvertently set up the cron events twice by correcting the post after they’ve published it: both events fire the ‘publish_post’ event, which the mailing list uses to start it’s process.

However it happens, the possibility is that two events could have exactly the same timestamp and arguments, which would mean that one overwrites the other in the array. To avoid this, I included the slight randomization, which only alters the schedule by a maximum of three minutes.

So, now let’s look at the entire process in code (with some pseudo-code) in order to understand how everything fits together:

//======================================
// Description: Our scheduling function.  Sends the email in 50-recipient chunks
function bhd_schedule_message($sender_email, $subject, $list, $headers, $mailtext) {
// Establish our random time interval between the publishing of the post and the first email blast:
	$time = rand(1000, 1200);
	for($x = 0; $x  $sender_email,
				'subject' =&amp;gt; $subject,
				'mail_text' =&amp;gt; $mailtext,
				'send_headers' =&amp;gt; $send_headers
			);
// schedule the email blast, increment the $time variable by twenty minutes for the next loop:
			wp_schedule_single_event(time()+$time, 'bhd_cron_send_hook', $mail_bundle);
			$time = $time + 1200;
		}
	}
}

//======================================
// Description: here is our actual cron job.
function bhd_cron_send($sender_email, $subject, $mail_text, $send_headers) {
	wp_mail($sender_email, $subject, $mail_text, $send_headers);
}

//======================================
// Description: Finally, here is our custom action.
add_action( 'bhd_cron_send_hook', 'bhd_cron_send', 10, 4 );

Note: we have not included the function that will be called when the post is published, because of course we’re not dealing with adding actions onto that particular hook. But for reference’s sake, such a function would look something like this:

add_action('publish_post', 'my_mailinglist_function');