New WordPress Plugin: Send System Info

Standard

When providing support for WordPress do you waste time going back and forth with the client asking for information about their website? Information like versions of WordPress, PHP and MySQL, as well as configuration of each can be invaluable to diagnosing a problem quickly and accurately.

After some work with the team on the Stream Plugin, I realized the need for users to easily gather information about their site’s configuration. This idea gave birth to the Send System Info Plugin for WordPress.

This plugin not only displays the site information, it allows the user to send this data to you in three forms:

  1. A downloadable plain text document, handy for archiving for later comparison
    SSI-Top
  2. An email generated within the plugin
    SSI-Email
  3. A URL that allows secure remote viewing of this data
    SSI-Remote-Viewing

My favorite feature of this plugin is the remote viewing URL, which points to a plain text page of the site information. The cool thing is that this URL can be regenerated, wiping out access to those who have the previous URL.

This is especially handy for posting site information in support forums, where you have no contact email address with those providing support, like the WordPress.org Support Forums. This link can be posted to the forums, and when you are done getting the support you need, simply regenerate the URL and later visitors to the forum will have no access to your information. This is a much more secure method of getting help than simply writing your server information into the forum.

I’ve had some great help from Frankie Jarrett and Luke Carbis and Pippin Williamson on Send System Info. You can help out, too! I have a GitHub repo available at https://github.com/johnregan3/send-system-info, and contributions are always welcome. I want this plugin to be as useful as possible for the WordPress community.

I invite you to download Send System Info and give it a test run. I think you’ll be surprised at how much information is available, as well as it’s simplicity and how easy it is to use. Give it a shot!

Adding Support for Vertical Featured Images in WordPress Themes

Standard

Here’s a quick and easy way to add support for Vertical Featured images in your WordPress themes.

How it Works

Using a filter hook, we’re going to add a class to the Featured Image markup if the image’s height is greater than its width. Then, using CSS we’ll float it to the right. Pretty simple, huh?

The Script

In your functions.php file add:

The CSS

This code floats the image to the right and adds some margin to separate the image from the post’s content. Be sure to include it in your CSS file.

.vertical-image {
	float:right;
	margin-bottom: 1em;
	margin-left: 2%;
	max-width: 33%;
}

Caveats

Your Post title will need to be clear: none; if this happens:

Screen Shot 2014-01-02 at 2.13.46 PM

You may need to add overflow:hidden to your post’s CSS to prevent this from happening in case the post excerpt is too short:

Screen Shot 2014-01-02 at 1.59.06 PM

The Result

This is what it should look like if the Feature Image has a vertical (portrait) orientation:

Screen Shot 2014-01-02 at 1.53.14 PM

Conclusion

When researching this, I had a hard time finding resources on this topic, so I hope this helps! If you find this post helpful, be sure to pass it on, including a link to this post!

Scheduling Events with WordPress Tutorial

Standard

There are many reasons to schedule events; maybe you need to check an external API, or prehaps periodically empty a cache. This process can even be used for something as simple as making a post un-sticky after a certain length of time.

Scheduling Events in WordPress is basically running a Cron, just simplified by WP. There are several functions available as tools for this process, and here we’re going to take a look at three of them:

In the code at the bottom of this post is a plugin that you can insert into your wp_content/plugins folder. It will produce an admin menu item entitled “Schedule Event Tutorial.” This plugin is very simple: You check a checkbox and set a time for it to become unchecked. After that time has passed, the checkbox will become unchecked.

Screen Shot 2013-11-11 at 10.23.07 PM

The Process

The way the plugin works is that when the checkbox is checked and a time is selected, a function is run that does two things:

1. Checks to see if another “unchecking” event is already scheduled. If so, it removes that event.

2. Schedules the new “unchecking” event.

How it Works

First of all, The actual unchecking is done by the function uncheck_checkbox(). You will see around line 44 that we have hooked that function into the action we’re calling ‘uncheck_event_hook’.

This is just like any other add_action(). The array(__CLASS__….) part is a part of the Object-Oriented Programming we’re using. It tells the add_action function that we’re referencing a method (funciton) inside this class.

add_action( 'uncheck_event_hook', array( __CLASS__, 'uncheck_checkbox' ) );

The interesting part is teling ‘uncheck_event_hook’ when to fire.

Now, follow along in the schedule_event() function…

First, check to make sure process only runs if the “Save Changes” button has been pressed.

if ( ! isset( $_GET['settings-updated'] ) )
    return;

Then a check is run to see if an event exists on this hook. It gets the next time this hook is scheduled to fire by using wp_next_scheduled. Then, if any value is returned, wp_unschedule_event is used to unschedule that event from the ‘uncheck_event_hook’.

// Remove any scheduled event on this hook
    $scheduled_event = wp_next_scheduled( 'uncheck_event_hook' );
    if ( $scheduled_event !== false ) {
        wp_unschedule_event( $scheduled_event, 'uncheck_event_hook' );
    }

Next, we do the actual scheduling. We grab the value of our expiration time setting, and if it’s a number, we set the time the ‘uncheck_event_hook’ will run.

// Schedule the event to uncheck the checkbox
    $exp_time = self::expiration_time();
    if ( is_numeric( $exp_time ) ) {
        wp_schedule_single_event( $exp_time, 'uncheck_event_hook' );
    }

At this point, we wait until the time passes our expiration time, then on the next time any page is viewed on your site, the ‘uncheck_event_hook’ will fire off uncheck_checkbox(), as we set in the add_action() at the beginning of the process.

The Full Plugin Code

I decided to write this as a fully-featured plugin for a few reasons:

  1. An installable plugin would help make this process a bit more practical
  2. I learn best by viewing others’ code. Maybe you can learn something new by looking at how another developer does it

By presenting the full code, less-experienced developers have the chance to learn more about Object-Oriented code, as well as what it takes to set up a Plugin. Of course, if you have any questions about what’s going on, be sure to hit me up. I’d love to help you on your way to learning something new!

<?php
/**
* Plugin Name: Schedule Event Tutorial
* Description: Schedules single event to uncheck a checkbox on a custom Menu page
* Version: 0.1
* Author: John Regan
* Author URI: http://johnregan3.me
* Text Domain: setjr3
* License: GPLv2+
*/
/**
* Copyright (c) 2013 X-Team (http://x-team.com/)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2 or, at
* your discretion, any later version, as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* This class is where you can see Scheduled Events in action.
*
* Here, the old scheduled event is unscheduled and the new event is set.
*/
class Schedule_Event_Tutorial {
/**
* Registers our 'uncheck_event_hook" action hook that fires our un-checking action.
*/
static function setup() {
// Scheduled Event Hook
add_action( 'uncheck_event_hook', array( __CLASS__, 'uncheck_checkbox' ) );
}
/**
* On settings save, Update scheduled event time.
*
* Begin by removing any scheduled event on our 'uncheck_event_hook' hook.
* Then, we format the saved time to Unix.
* Finally, we schedule the event.
*
* Typically, this could be fired when a post is saved ('save_post' hook, for instance),
* but it's not really that easy on an options page like this one.
*
* @action 'load-{page}'
* See Schedule_Event_Tutorial_Setup::menu_page()
*/
static function schedule_event() {
if ( ! isset( $_GET['settings-updated'] ) )
return;
// Remove any scheduled event on this hook
$scheduled_event = wp_next_scheduled( 'uncheck_event_hook' );
if ( $scheduled_event !== false ) {
wp_unschedule_event( $scheduled_event, 'uncheck_event_hook' );
}
// Schedule the event to uncheck the checkbox
$exp_time = self::expiration_time();
if ( is_numeric( $exp_time ) ) {
wp_schedule_single_event( $exp_time, 'uncheck_event_hook' );
}
}
/**
* Fetches and formats the saved expiration time
*
* Get expiration time from options, set it according to the local timezone
* then format it as Unix time.
*
* @return int $time Unix-formatted time string
*/
static function expiration_time() {
$exp_time = get_option( 'setjr3_time' );
$time = ( ! empty( $exp_time ) ) ? $exp_time : null ;
// If no time is set, exit.
if ( is_null( $time ) )
return false;
$tz_local = new DateTimeZone( get_option( 'timezone_string' ) );
$time = new DateTime( $time, $tz_local );
$time = (int)$time->format('U');
return $time;
}
/**
* The actual un-checking of the checkbox
*
* Fires on 'uncheck_event_hook' action, set in setup() above.
*
* @action 'uncheck_event_hook'
*/
static function uncheck_checkbox() {
update_option( 'setjr3_checkbox', null );
}
} // End class Schedule_Event_Tutorial
/**
* Set up the Tutorial
*
* The content of this class simply sets up the tutorial form.
* If you find it confusing, just ignore it.
*/
class Schedule_Event_Tutorial_Setup {
/**
* Add our actions/filter
*/
static function setup() {
$plugin = plugin_basename(__FILE__);
add_filter( "plugin_action_links_$plugin", array( __CLASS__, 'tutorial_link' ) );
// add action for Menu page
add_action( 'admin_menu', array( __CLASS__, 'menu_page' ) );
// add action for Settings
add_action( 'admin_init', array( __CLASS__, 'settings' ) );
// Run our Schedule_Event_Tutorial class above
Schedule_Event_Tutorial::setup();
// Clean up when this plugin is uninstalled
register_uninstall_hook( __FILE__, array( __CLASS__, 'uninstall' ) );
}
/**
* Print direct link to Tutorial Admin Page
*
* Fetches array of links generated by WP Plugin admin page ( Deactivate | Edit )
* and inserts a link to the Custom CSS admin page
*
* @param array $links Array of links generated by WP in Plugin Admin page.
* @return array $links Array of links to be output on Plugin Admin page.
*/
static function tutorial_link( $links ) {
$settings_page = '<a href="' . admin_url( 'admin.php?page=schedule-event-tutorial.php' ) .'">Tutorial</a>';
array_unshift( $links, $settings_page );
return $links;
}
/**
* Create Admin Menu Page
*
* Also fires action to Schedule the Event when page reloads.
* see Schedule_Event_Tutorial::schedule_event()
*/
static function menu_page() {
$slug = add_menu_page( __( 'Schedule Event Tutorial', 'setjr3' ), __( 'Schedule Event Tutorial', 'setjr3' ), 'manage_options', basename(__FILE__), array( __CLASS__, 'render_tutorial_page' ) );
add_action( 'load-' . $slug, array( 'Schedule_Event_Tutorial', 'schedule_event' ) );
}
/**
* Settings Page setup
*
* For more info: http://codex.wordpress.org/Settings_API
*/
static function settings() {
register_setting( 'setjr3_settings_group', 'setjr3_checkbox' );
register_setting( 'setjr3_settings_group', 'setjr3_time' );
add_settings_section( 'setjr3_section', '', '', __FILE__ );
add_settings_field( 'checkbox', __( 'Checkbox', 'setjr3' ), array( __CLASS__, 'checkbox' ), __FILE__, 'setjr3_section' );
add_settings_field( 'expiration_time', __( 'Expiration Time', 'setjr3' ), array( __CLASS__, 'expiration_time' ), __FILE__, 'setjr3_section' );
}
/**
* Render Checkbox
*/
static function checkbox() {
$checkbox_value = get_option( 'setjr3_checkbox' );
echo '<input type="checkbox" name="setjr3_checkbox" value="1" ' . checked( 1, $checkbox_value, false ) . '/>&nbsp;&nbsp;<span class="description">This checkbox will uncheck after the expiration time set below.</span>';
}
/**
* Render Date Input
*/
static function expiration_time() {
$exp_time = get_option( 'setjr3_time' );
$exp_time_value = ( ! empty( $exp_time ) ) ? $exp_time : substr_replace( current_time( 'mysql' ), 'T', 10, 1 ) ;
echo '<input name="setjr3_time" type="datetime-local" value="' . $exp_time_value .'" step="1" />';
}
/**
* Render Menu Page
*/
static function render_tutorial_page() {
$curr_time = substr_replace( current_time( 'mysql' ), 'T', 10, 1 );
$exp_time = get_option( 'setjr3_time' );
$exp_time_value = ( ! empty( $exp_time ) ) ? $exp_time : 'Not set' ;
?>
<div class="wrap">
<h2><?php echo __( 'Schedule Event Tuorial', 'setjr3') ?></h2>
<?php if ( isset( $_GET['settings-updated'] ) ) { ?>
<div id="message" class="updated">
<p><strong><?php _e( 'Settings saved.', 'setjr3' ) ?></strong></p>
</div>
<?php } ?>
<p>
<?php _e( 'This is a tutorial demonstrating how to Schedule Events.<br />Begin by checking the checkbox below, then set a time for the checkbox to become unchecked.<br />Then, on the next page refresh after that time, the checkbox will be unchecked.', 'setjr3' ) ?>
</p>
<p>
<code><?php _e( 'Current Time', 'setjr3' ) ?>: <?php echo $curr_time ?></code><br />
<code><?php _e( 'Uncheck Time', 'setjr3' ) ?>: <?php echo $exp_time_value ?></code>
</p>
<form method="post" action="options.php" enctype="multipart/form-data">
<?php settings_fields( 'setjr3_settings_group' ) ?>
<?php do_settings_sections( __FILE__ ) ?>
<?php submit_button() ?>
</form>
</div>
<?php
}
/**
* Cleanup on unistnall
*/
static function uninstall() {
unregister_setting( 'setjr3_settings_group', 'setjr3_checkbox' );
unregister_setting( 'setjr3_settings_group', 'setjr3_time' );
}
}
add_action( 'plugins_loaded', array( 'Schedule_Event_Tutorial_Setup', 'setup' ) );

Tutorial: Writing a Unit Test for WordPress

Standard

WordPress Unit Testing is an expanding field in the WP world. Currently, there is a big push to add more tests, and you can get in on the action!

If you need help getting started with Unit Testing, check out my previous post about why to get into WordPress Unit Testing. It includes a short section about how to get rolling.

The Basics

The way the test works is relatively simple: a developer makes changes to a WordPress file, then runs the tests to make sure nothing breaks because of their changes. It notifies the dev if he/she needs to change something in their code.

Think of tests as a way to prove that a function still works properly. It tests for the expected output, so sometimes writing a test isn’t very satisfying because, in theory, your test will cause no reaction because everything is working fine.

Important

Unit tests are written to ensure WordPress returns the expected output. To be clear, they are not written to specifically test one’s code, meaning you don’t usually write a test for the sole purpose of testing your new code. To put it another way, Unit Tests exist to prove WordPress works, and if you run your code against all of the WP Unit Tests, you can see if your edits affect the output that WP creates, you can see if it breaks WP’s basic functionality.

A Basic Unit Test

Unit Tests are written then submitted to WordPress Trac to be included in the repository of Unit Tests. They can be very basic or very complicated, but they always seem to do obvious things; they make sure input is processed into the expected output.

I think this can best be explained through example. The following Unit Text will almost work. For the sake of clarity, I’ve left out some of the class information, but check it out as an example to get an idea of how a test is written.

/**
 * Test to see if get_post works.
 *
 * Compares a post ID ($org_post_id) with post ID 
 * taken out of get_post ($new_post_id).
 * If they don't match, get_post() doesn't work, and it will 
 * return an error.
 */
function test_get_post() {
    //Create new post using method provided by WP
    $org_post_id = $this->factory->post->create();
 
    //get post object using the new post's ID
    $post_obj = get_post( $org_post_id );

    //Get the post ID as given to us by get_post
    $new_post_id = $post_obj->ID;

    //Use pre-defined method to test if the two ID's match
    $this->assertEquals( $org_post_id, $new_post_id );
        
}

The purpose of this test is to see if the mechanism of get_post() is broken by whatever changes you submit with your new code. This test should work fine, as long as get_post() works. If it fails, then you know you’ve done somthing to mess up get_post().

A Full-Featured Example

This is a Unit Test I’m working on for the upcoming save_post{$post_type} action hook. The hook is all ready, and this test that I have written just needs to be tested. So, it may have some errors, but it will give you the general idea of how a test works. Check out the comments in the Gist for information on how it works. To fully understand it you’ll need to have a good grasp on Object Oriented Programming (OOP).

If the font size bothers you as much as it does me, you can view the gist here.

<?php
/**
* This has been tested using the diff for save_post_{$post_type}
* https://core.trac.wordpress.org/changeset/25050
*
* @todo Try different post type names
* @todo Create two posts and try to update them both at the same time
*/
class Save_Post_Post_Type {
/**
* Loads our control data into the test post.
*
* First, we'll save the post using wp_update_post and without save_post_{$post_type}
* We'll use this as a control to compare our $result against later.
* We can't just compare the raw post input with the save_post_{$post_type} because saving
* the post strips slashes, validates, etc.
*
* @param int $test_post_id ID of the post we are testing with.
* @return string $control_result content of post saved with wp_update_post
*/
function control_test( $test_post_id ) {
$control_post = array();
$control_post['ID'] = $test_post_id;
$control_post['post_type'] = 'book';
$control_post['post_content'] = "Initial Data.";
$control_post_id = wp_update_post( $control_post );
/**
* when we save it, it returns a post ID,
* so we'll need to get our content back out of the control post.
*/
$control_post = get_post( $control_post_id);
$control_result = $control_post->post_content;
return $control_result;
}
/**
* Test to see if save_post_{$post_type} hooks into the saving action correctly.
*
* @param int $test_post_id ID of the post we are testing with.
* @param string $input Input from provider() method.
* @return string $test_result Content of post(s) updated with save_post_{$post_type}
*/
function test_save_post_post_type( $test_post_id, $input ) {
//Run the hook that should exist, and attempt to change the data.
add_action( 'save_post_book', $this->modify_content( $test_post_id, $input ) );
//Get post content by id
$result_post = get_post( $test_post_id );
$test_result = $result_post->post_content;
return $test_result;
}
/**
* Save our post with new post_content, on 'save_post_book'
*
* @param int $test_post_id ID of the post we are testing with.
* @param string $input Input from provider() method.
* @return void
*/
function modify_content( $test_post_id, $input ) {
/**
* Attempt to overwrite 'the auto-generated post_content' with $input
* This will be done on 'save_post_book' in test_save_post_type()
*/
$test_post_new_content = array();
$test_post_new_content ['ID'] = $test_post_id;
$test_post_new_content['post_content'] = $input;
wp_update_post( $test_post_new_content );
}
}
/**
* Set Up Test
*/
class Tests_Save_Post_Post_Type extends WP_UnitTestCase {
/**
* Establish testing variables
*
* This information will be passed as the parameter ($input) into our test method in this class.
* Note that we use @dataProvider to let test_save_post_post_type know we're using this method
* Note also that it returns a multidimentional array (an array within an array).
* These values will be passed one at a time into our testing function
*
* @return array Array of input content to test "save_post_{$post_type}"
*/
public function provider() {
return array(
array( '023983775900' ),
array( '(*&@#)*)&*#@*~_)*?(#' ),
array( 'http://wordpress.org&#39; ),
array( 'It\'s a trap!' ),
array( 'These aren\'t the droids you are looking for.' ),
array( 'I find you lack of faith disturbing.' ),
array( 'Ready are you? What know you of ready?' ),
);
}
/**
* Tests to ensure save_post_{$post_type} hooks into save_post correctly.
*
* If these $input and $result don't match, the test will throw an error.
* They should match, displaying that the input matches the output ($result),
* when saved by "save_post_{post_type}".
*
* This function must begin with "test".
*
* @param string $input Input from provider()
* @return bool Pass/Fail test Results
*
* The following line is required to let the test know what method is sending the $input
* @dataProvider provider
*/
public function test_run_test( $input ) {
$test = new Save_Post_Post_Type;
//Create Post using extended class WP_UnitTestCase
$test_post_id = $this->factory->post->create();
$control_result = $test->control_test( $test_post_id );
/**
* Run the test. Send the input, return the result.
*
* This method changes the content of our post into a Star Wars quote.
* If it does not change, it will thow an error. Remember, our original content was,
* "Initial Data."
*/
$test_result = $test->test_save_post_post_type( $test_post_id, $input );
/**
* Compare $control_result content with $test_result content.
*
* Since we attempted to modify the content of the "Book" post, this method "assertFlase"
* checks to see that these two statements do not match.
* There are many other methods you can use here:
* http://phpunit.de/manual/current/en/appendixes.assertions.html
*/
$this->assertNotEquals( $control_result, $test_result);
}
}

A Note

Weston Ruter added a comment greatly simplifying this process, which would be the best way to run this test. One of its greatest benfits is that it is extremely compact, although a bit advanced for a less-experienced developer, which may be you. I’ve decided to leave “the long way” to run this test here in this post so you can view more of the features and techniques of Unit Testing.

More

I hope you were able to learn something from viewing the actual code from a Unit Test. I’ve found that always helps me much more than reading long explanations. However, if you have any questions, I’m glad to answer them. Unit Tests are a real need in WordPress, and I’d love to help the community by assisting future Test authors.

Why you need to get into WordPress Unit Testing

Standard

What is Unit Testing?

A non-technical definition of Unit Testing, is “a way to make sure your code won’t break stuff.” In the WordPress world, it means using a provided library of tests to check if your code will return errors. Basically, it’s a program that you put your code files into, and when you run it, you will be notified if there are problems.

It’s important to know that WordPress Unit Testing is typically done with PHPUnit. You’ll see that name thrown around here and there when researching this topic.

Why Automated Testing?

Holy crap, WHY NOT?

In the past, I wasted a lot of time clicking back and forth between posts and settings pages to ensure that my new plugin or theme wouldn’t throw any errors in a WordPress install. Now, you can just type a few commands and your code will be analyzed quickly and thoroughly. Automated Unit Testing lets you quickly check your code in many, many different contexts and scenarios.

Unit Testing has proven invaluable to me in my attempts to write patches for WordPress Core. One time I ran some code I thought would work, and it broke WordPress in 9 other places. Luckily I was able to test it before I submitted it for review, right?

Unit Testing can also reveal unexpected output. Recently, automated testing revealed to me something I didn’t expect to see: when typical date formats were passed into a function, it executed perfectly, but when just one other date format was sent through, it spit out data I hadn’t accounted for. I’m not sure I would have caught that error on my own. It would have required running my code by hand many times and many ways.

Typically, testing looks like this:

  1. Edit code.
  2. Switch to browser.
  3. Input data (e.g., create a new post/comment, ativate a plugin, change admin settings, etc).
  4. Cross fingers.
  5. Check 80,987,096 places for error messages.
  6. Hope you didn’t miss anything.
  7. Repeat 9,464,643,943 times, just to be sure.
  8. Spend the rest of your day answering support questions about errors caused by your last Plugin.

The automated testing process looks more like this:

  1. Edit code.
  2. Switch to terminal.
  3. Start unit testing ($ phpunit).
  4. Make a glass of ice tea.
  5. Wait for Test Results.
  6. ???
  7. Profit.

How to Test your WordPress App, Theme, Plugin, or Patch

Tom McFarlin wrote a great series on this topic, so I won’t waste time and space trying to explain it all over again (and probably in an inferior way). Check his article out on WPTuts+: The Beginner’s Guide to Unit Testing: What Is Unit Testing? Specifically, check out the section titled “Preparing the Environment.” It will help get you started.

Other Resources

Conclusion

Unit testing is very important. It is way faster and way more thorough than you are. It is powerful and will save you a lot of headaches and embarassment when your code goes live. You need to get into Unit Testing.

Your Turn

So, what has been your experience with Unit Testing? What tips do you have for those getting started? Share your thoughts in the comments!

If you’re ready to get to the meat and potatoes, check out the next post, “Tutorial: Writing a Unit Test for WordPress.”