Plugin Resource for WordPress and WPMU
WP Crons Made Easy

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:

Code:
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

Code:
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);
}

Code:
<?php if($jobs = bhd_get_schedule()) { ?>
<h3>Currently Scheduled Deliveries</h3>
<p>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.</p>
   <form name="bhd_ml_schedule" method="post" action="options-general.php?page=bhd_mailinglist.php">
       <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"/>
      <table class="form-table">
         <?php foreach($jobs as $job=>$args) { ?>
         <tr>
            <td><?php echo(date('l jS \of F Y h:i:s A', $job) ); ?gt;</td>
            <td><?php print($args['subject']); ?></td>
            <td><input type="checkbox" value="<?php echo($job); ?>" name="cron_job[]" /></td>
         </tr>
         <?php } ?>
         <tr>
            <td colspan="3" align="right">
               <input type="hidden" id="bhd_ml_cron-submit" name="bhd_ml_cron-submit" value="1" />
               <input type="submit" value="Delete Jobs" />
            </td>
         </tr>
      </table>
   </form>
<?php } ?>

Tags:

Scheduling with WordPress Cron Functions

I’ve been struggling to understand exactly how the cron functions of WordPress work because I’ve been working on a plugin for some friends of mine that need a mailing list manager. Since I’ve now gotten a handle on the situation, I figured it was time to spread that knowledge for the benefit of those just coming to the awesome power of WP-Cron!

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 A, B, C.

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. We need to not let our posts be blocked as spam, and so we need to throttle the messages by sending blasts every twenty minutes (or so, 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

Code:
my_scheduled_function(<em>[$arg1, [$arg2, [$arg3. . . ]]]</em>)

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:

Code:
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:

Code:
add_action('my_custom_hook', 'some_function'<em>[, $priority, [$num_args]]</em>);

WordPress assumes that anything added as the first argument exists without any additional registration of that new hook. In other words, this actually is the declaration of an action hook. It associates the second argument, which should be a valid function name, with the first whenever the first 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. This 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 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.

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. 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:

Code:
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:

Code:
$mail_bundle = array(
     'sender_email' => $sender_email,
     'subject' => $subject,
     'mail_text' => $mailtext,
     'send_headers' => $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:

Code:
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:

Code:
$time = rand(1000, 1200);

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

Well, it is possible - however improbable - that the event could be scheduled at the exact same time as another event. 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. It’s possible to have scheduled the same event to happen at the same time with the same arguments - again, improbable but possible. In our example, it’s possible that someone could inadvertently set up the cron events twice by correcting the email after they’ve published it. To avoid this, I included the slight randomization, which only alters the schedule by about three minutes or so.

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

Code:
//======================================
// Description: Our <a href="#schedule">scheduling function</a>.  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' => $subject,
				'mail_text' => $mailtext,
				'send_headers' => $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 <a href="#cron">cron job</a>.
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 <a href="#hook">custom action</a>.
add_action( 'bhd_cron_send_hook', 'bhd_cron_send', 10, 4 );

Tags: , , , , ,

Testing Code Markup

I’ve downloaded and installed a plugin that is designed to display code in a more helpful way, not ironically called “Code Markup,” and I’m playing around to see how things work. It’s supposed to be able to differentiate between different types of code and color-code the markup accordingly. Let’s find out.

Code:
     <p>This is a paragraph inside a basic HTML wrapper.</p>

Code:
     <p>This is a paragraph inside a basic HTML wrapper.</p>

Code:
foreach($boogers as $booger) {
      flick($booger);
}

Tags: , , ,

Using get_option() to Determine the Current Theme

This may be apropos of nothing for a lot of people, but just as a helpful little hint, you can get the theme used by a blog by calling the get_option() method thusly:

Code:
get_option('current_theme');

I’m using this on a plugin that changes the colors of certain objects (not CSS directable) by checking the current theme and setting matching colors. Think “custom color YouTube video window.”

MU admins, you could use get_site_option() if you’re planning on doing something global.

Tags: , , , ,

Oyie, With the Conflicts!

There seems to have been a small problem with the Title to Tags plugin, as I inadvertently created a function without an “hn_” prefix.  So, there was a conflict with another plugin.  So, I’m recommitting a new version of the Title to Tags plugin, version 1.2.

Tags: , ,

The Titles to Tags Plugin

Download Plugin

Never forget to post your tags again! With the Titles to Tags plugin, WordPress will scan your post’s title for the most keyword-appropriate words and add them to your tags list automatically on save! The plugin comes with a very long list of words which are statistically irrelevant, such as “I” or “among,” which don’t make good keywords. You can also edit this list to include more or less terms by going to Options > Titles 2 Tags. There is also an option to revert to the original list, but watch out! You will lose your edited list forever if you do this!

This plugin will only save tags to your posts if none exist already. The presumption is that if you’ve already saved tags, you must know what you want and anyway, you might have removed some tags Titles to Tags added because you don’t like them. The function runs every time you save a post, either by publishing, saving or editing the post.

To install the plugin, simply download it from the WordPress Plugin Repository, then extract both the hn_titles_to_tags.php file and then hn_t2t and it’s contents into your /plugins folder. Activate the plugin by going to Plugins, and begin posting as normal.

WordPress MU Developers: Note that, if your creating a community-based website built on MU - and your users aren’t being diligent about adding tags - this is a great way to get those tags created painlessly! You can place the file and it’s companion directory in the /mu-plugins file and each blog will get it’s own editable list of ignored words.

Tags: , , ,

The Theme Styles Plug-n-Hack

Download Here

This one’s called a “Plug-n-Hack” because it relies on a lot more than just the basic plugin to work. It requires careful layout of your blog’s theme and editing the plugin in order to work correctly.

But for those of you who are running sites where you’d like to be able to keep the overall structure of individual blogs the same, while allowing your users to make some adjustments to the color scheme, this may be just the solution. With the Theme Styles Plug-n-Hack, you can create new color schemes as the administrator and offer them to your users to select from.

The plugin itself is very basic. All it does is add a new style element to the wp_head when the site loads. The elements within the style must be the same IDs and classes as you use regularly, because the new declarations will override your default settings for color. As such, it is imperative that before working with this plugin, you take stock of your current color scheme and determine how many colors you work with ordinarily and how many of those you’ll want to change. For example, my site uses five basic colors not counting the text, which I decided would always stay the same color.

Once you’ve determined what colors you’re going to change, you need to start creating a second style sheet (which will eventually replace the one in the plugin) that calls each of the affected elements and declares only those attributes which are related to color. By way of example, in my case:

Code:
#top_nav_1 {
background:#CD2626 none repeat scroll 0%;
border-bottom:1px solid #FFCC00;
border-top:1px solid #FFCC00;
}

Now that you have this list, you can begin to replace the RGB color codes with PHP substitutions. The plugin is currently setup to recognize five color variables:

Code:
$darkest, $dark, $medium, $light, $border

These variable names made sense to me, but feel free to edit the plugin to change them, if you prefer. Go ahead and start putting in your substitutions where they fit. In my case:

Code:
#top_nav_1 {
border-bottom: 1px solid <?php echo($borders); ?>;
border-top: 1px solid <?php echo($borders); ?>;
background:<?php echo($dark); ?>;
}

Now you can go ahead and insert that style list in place of the one in the plugin, starting at line 33 of the theme_styles.php file. Note also that at line 23, some default values are set. You’ll want to switch these around to your theme’s default colors! Defaults are also set for the admin area at line 208.

Once all this is done, you can upload the theme_styles.php file to your /plugins folder and test it out. By activating the plugin, you create two new menu items: one is for Site Admins only, which is located under the Site Admin tab and allows you to either create new styles or change the values of existing styles; the second allows users to select from the styles you’ve created and is located under the Presentation menu.

With a little bit of editing, I think you’ll find this a welcome addition to your website.

Tags: , ,

get_admin_users_for_domain( $sitedomain = ”, $path = ” )

Summary:  Returns an array of all users who have Administrative roles on a given domain.

Detail:  This function checks the database for all Site Admin users on the specified domain ($sitedomain and $path).  The returned value is an associative array of usernames and passwords.  This function works not only site-wide but across multiple domains, if you’re environment is set up in that way.

Tags: , ,

restore_blog()

Summary: A companion function to the switch_to_blog() function, this function takes the settings stored in the temporary array setup by switch_to_blog() and swaps them back, thus restoring the original blog.

Detail: This very simple function simply undoes what was done in the switch_to_blog() function. It restores the settings to the $wpdb function and user roles that were held in the temp array setup by the switch_to_blog() function.

As previously noted in the switch_to_blog() post on this site, you cannot expect the temp array to hold more than one blog’s settings, thus if you are iterating over several blogs, you’ll need to either continuously use restore_blog() to get back to the original blog in between iterations, or else use switch_to_blog() to go back to the original, ignoring the temp array altogether.

Tags: ,

switch_to_blog( $new_blog )

Summary: Toggles all settings to a different blog’s settings, thus allowing you to perform functions as though you were on that blog. Stores the current blog’s settings in a temporary array, so they can be restored when desired.

Detail: This function switches all the settings in the $wpdb object to the new blog ($new_blog), while also updating the current user’s role as per the new blog. The end result is that all operations performed after using the switch_to_blog function will behave as you would anticipate if you were on that new blog. You would presumably use this function along with the restore_blog() function.

This is probably the single most important and strangely, least-used function in the WPMU platform. The advantages of using switch_to_blog are many. For one, it saves you the trouble of having to rewrite the database blog prefix (wp_24_posts, for example), and instead let’s you query the dB using the same pseudonyms you would use in any WordPress setting ($wpdb->posts). For another, you can use all the standard, built in WordPress functions such as get_permalink() exactly as you would in any other setting. Best of all, because it stores all the current blog’s settings in a temporary array, you can quickly restore the settings without a lot of complex coding.

Additionally, since the user’s roles are carried over, and because you’re using the standard WordPress functions, information normally not visible to a user remains invisible without complex coding. For example, if you want to display the most recent posts from a given blog, you can simply switch to that blog, use the get_posts() function to perform whatever tasks you’d like, and switch back. In doing this, you can avoid showing posts from private, spam or adult blogs. Moreover, on each blog, you can eliminate the possibility that visitors could see private or unpublished posts, or that posts set to be visible only to certain user levels. However, users who do have the proper privileges on that second blog will be able to see those additional posts. Thus you can write supple, flexible plugins that play ball with most available plugins on all public blogs. No additional coding is required, nor are lengthy database queries.

A word of warning: do not attempt to use this function to iterate over a variety of blog ID’s hoping to restore the original blog’s ID at the end, unless you switch back to the original blog in between. At first, I thought that this would be possible, but it is not. Thus, swapping back and forth between several blogs would need to be done like this pseudo-code:

Code:
switch_to_blog(4)
. . . do some stuff . . .
restore_blog()
switch_to_blog(5)
. . . do some stuff . . .
restore_blog()

Tags: , ,

Next Page »

Bad Behavior has blocked 33 access attempts in the last 7 days.

'