Code Snippet: Generate A Plaintext Data Table

Inspired by some awesome-as-always code written by my co-worker Mike Adams, here’s a helper function to help create plaintext data tables in PHP. This is particularly useful for PHP-based CLI scripts (we use quite a few at Automattic) or for plaintext e-mails.

But first, here’s an example output with the optional dividers enabled:

   ID | First Name     | Last Name   
-------------------------------------
    1 | Alex           | Mills       
    2 | John           | Smith       
    3 | Barack         | Obama       
    4 | Fred           | Flinstone   
12345 | ReallyLongName | IsReallyLong

Click here to keep reading and see the code »

Code Snippet: Helper Class To Add Custom Taxonomy To Post Permalinks

Say you have a custom taxonomy called “sport” and you wanted to inject “football” into the permalink of posts that have the “football” term assigned to it. How would you go about doing that? Well below is a helper class that will make it easy to accomplish exactly that.

Click here to read the rest of the post and see the code »

Code Snippet: Add A Link To Latest Post To WordPress Nav Menu

Someone on #wordpress IRC support channel was trying to add link to their latest blog post to their WordPress navigation menu, so I threw together a few lines of code to help them accomplish this.

// Front end only, don't hack on the settings page
if ( ! is_admin() ) {
	// Hook in early to modify the menu
	// This is before the CSS "selected" classes are calculated
	add_filter( 'wp_get_nav_menu_items', 'replace_placeholder_nav_menu_item_with_latest_post', 10, 3 );
}

// Replaces a custom URL placeholder with the URL to the latest post
function replace_placeholder_nav_menu_item_with_latest_post( $items, $menu, $args ) {

	// Loop through the menu items looking for placeholder(s)
	foreach ( $items as $item ) {

		// Is this the placeholder we're looking for?
		if ( '#latestpost' != $item->url )
			continue;

		// Get the latest post
		$latestpost = get_posts( array(
			'numberposts' => 1,
		) );

		if ( empty( $latestpost ) )
			continue;

		// Replace the placeholder with the real URL
		$item->url = get_permalink( $latestpost[0]->ID );
	}

	// Return the modified (or maybe unmodified) menu items array
	return $items;
}

Place the above code in a plugin or just in your theme’s functions.php file.

Create a new “Custom Link” menu item where the URL is #latestpost (which will act as a placeholder and target for the code). You can title the item whatever you please. This code will then find that menu item (by looking for the placeholder URL) and then replace it’s URL with that of the latest post’s URL. Since the filter is before the code that adds the selected CSS classes runs, the menu highlighting will even work. When you visit the latest post on your blog, this menu item will light up.

Rather simple and elegant. You gotta love WordPress hooks.

Translating WordPress Plugin Details

Plugin authors: did you know that you can allow translators to localize the plugin details that show up in the plugins list in the WordPress administration area? Your plugin’s name, description, and so forth? Well you can! It’s actually really simple to do and all you need to do is add one or two additional plugin headers to your file.

The first is Text Domain and this is the text domain for your plugin, i.e. the first argument that you are passing to load_plugin_textdomain().

The second one is Domain Path and is optional. It’s only needed if you store your translation files in a subfolder inside of your plugin’s folder.

Here’s an example load_plugin_textdomain() call from one of my newest plugins:

load_plugin_textdomain(
	'add-descendants-as-submenu-items',
	false,
	dirname( plugin_basename( __FILE__ ) ) . '/localization/'
);

That loads translation files from a subfolder called “localization” inside of my plugin’s folder. This turns into the following plugin header:

Plugin Name:   Add Descendants As Submenu Items
Plugin URI:    http://www.viper007bond.com/wordpress-plugins/add-descendants-as-submenu-items/
Description:   Automatically all of a nav menu item's descendants as submenu items. Designed for pages but will work with any hierarchical post type or taxonomy.
Version:       1.1.0
Author:        Alex Mills (Viper007Bond)
Author URI:    http://www.viper007bond.com/

Text Domain:   add-descendants-as-submenu-items
Domain Path:   /localization/

An extra line break isn’t needed nor is the extra spacing but I added both just for aesthetic reasons.

And that’s it! WordPress will then attempt to translate the plugin’s name, URI, description, author, author URI, and version fields. I personally only include the plugin’s name and description in my translation template files though as I don’t feel translators need to localize the other fields.

If you need help generating a translation template file for your plugin, log into WordPress.org and then visit the “Admin” tab on your plugin’s page on WordPress.org. You can generate a POT file for your plugin there.

Editing Your Hosts File In Windows

Andrew Nacin mentioned here at WordCamp San Francisco 2011 where your hosts file is on Mac (and Linux), but I thought I’d point out where your hosts file is on Windows and how to change it so that you can point domain names to your local computer.

The easiest solution is to create a shortcut that will open Notepad and then tell it to open your hosts file. Right-click your desktop and select New → Shortcut. Paste the following into the field that pops up:

%windir%\system32\notepad.exe %windir%\System32\drivers\etc\hosts

Once you’re done creating the shortcut, right-click it and select “Properties”. Under the “Shortcut” tab is an “Advanced” button. Clicking it will show a checkbox for “Run as administrator”. This is important because otherwise you won’t be able to save your changes as the file is a protected one.

Opening it will show a little documentation and some examples. If you want to add your own, just add it to the bottom in this format:

	127.0.0.1	yourdomain.com

Saving the file and then visiting the domain in your browser will now show you files served by your local development environment. Note: a hard refresh may be required to due to your browser’s DNS cache. This can be accomplished by pressing control and F5.

This post was updated to recommend using a shortcut instead of manually opening Notepad as an administrator.

Accessing Other Databases/Servers From Within WordPress

Say you have a WordPress install where you need to pull in data from a separate database or server. The normal $wpdb instance of the wpdb class is limited to the database that your WordPress tables are located in. Yes, you could use mysql_connect() and the other standard PHP database functions, but then you don’t get all of the WordPress magic. So what to do?

The answer is surprisingly simple: make a new instance of the wpdb class! Dion Hulse (DD32) was kind enough to point out this great solution to me a few weeks back and I thought it was worth sharing with you all.

$myotherdb = new wpdb( $dbuser, $dbpassword, $dbname, $dbhost );

$myotherdb->get_results( "SELECT id, name FROM mytable" );

Changing Core WordPress Strings

One of the lesser known filters in WordPress is gettext. All strings that are run through the WordPress translation functions are also run through this filter after being translated thanks to a ticket I opened way back in 2008. That means you can actually use it to change pretty much any string in WordPress, namely in the admin area.

I’ve used this in some plugins I’ve written in the past and it works incredibly well.

My co-worker and one of the lead developers of WordPress Peter Westwood (westi) wrote a really good blog post about this, however I wasn’t completely happy with his code so I’m writing this blog post to share how I would write some code to take advantage of this versatile filter. Don’t get me wrong — he wrote good and valid code, but it’s not exactly the easiest thing for a novice to extend for their own uses.

function youruniqueprefix_filter_gettext( $translated, $original, $domain ) {

	// This is an array of original strings
	// and what they should be replaced with
	$strings = array(
		'View all posts filed under %s' => 'See all articles filed under %s',
		'Howdy, %1$s' => 'Greetings, %1$s!',
		// Add some more strings here
	);

	// See if the current string is in the $strings array
	// If so, replace it's translation
	if ( isset( $strings[$original] ) ) {
		// This accomplishes the same thing as __()
		// but without running it through the filter again
		$translations = &get_translations_for_domain( $domain );
		$translated = $translations->translate( $strings[$original] );
	}

	return $translated;
}

add_filter( 'gettext', 'youruniqueprefix_filter_gettext', 10, 3 );

So as you can see at it’s core it accomplishes the same thing as Peter’s code however my code should be a bit more clear on how to make it translate multiple strings.

Hope someone finds this helpful! 🙂

Easily Create XML In PHP Using A Data Array

I’ve been working with the Google Contacts API lately and needed to construct some XML as a part of that. PHP’s DOMDocument works really great for this, but it can get complicated fast as you need to create lots of variables for all of your elements, children, etc. It can make it quite hard to do dynamically.

An easier method would be to just convert an array into XML, the opposite of simplexml_load_string() if you will. So I wrote some code that does exactly that and I thought I’d share it incase anyone else would find it helpful. 🙂

Start by creating your array of data. I used a numeric array instead of an associative array because you can have duplicate elements in XML while you cannot have duplicate array keys.

$data = array(
	'name' => 'toplevelelement', // "name" required, all else optional
	'attributes' => array(
		'foo' => 'bar',
		'fruit' => 'apple',
	),
	'value' => 'Some random value.',
	array(
		'name' => 'achildelement',
		'value'=> 'Value',
	),
	array(
		'name' => 'anotherchildelement',
		'attributes' => array(
			'some' => 'attr',
		),
		array(
			'name' => 'grandchildelement',
		),
	),
);

An array such as that is easy to dynamically create based on your source data, it’s easy to read, and can be fairly easily manipulated.

name is the only required item of each array and it’s the tag name of the element. attributes is an array of attributes for the element and value is the value. Both of these are optional. Any children arrays are children elements and their structures should be the same.

Now here’s the code I wrote that converts the above array into some XML:

function generate_xml_element( $dom, $data ) {
	if ( empty( $data['name'] ) )
		return false;

	// Create the element
	$element_value = ( ! empty( $data['value'] ) ) ? $data['value'] : null;
	$element = $dom->createElement( $data['name'], $element_value );

	// Add any attributes
	if ( ! empty( $data['attributes'] ) && is_array( $data['attributes'] ) ) {
		foreach ( $data['attributes'] as $attribute_key => $attribute_value ) {
			$element->setAttribute( $attribute_key, $attribute_value );
		}
	}

	// Any other items in the data array should be child elements
	foreach ( $data as $data_key => $child_data ) {
		if ( ! is_numeric( $data_key ) )
			continue;

		$child = generate_xml_element( $dom, $child_data );
		if ( $child )
			$element->appendChild( $child );
	}

	return $element;
}

$doc = new DOMDocument();
$child = generate_xml_element( $doc, $data );
if ( $child )
	$doc->appendChild( $child );
$doc->formatOutput = true; // Add whitespace to make easier to read XML
$xml = $doc->saveXML();

You first create a blank canvas by creating a new instance of the DOMDocument class and then you pass the class instance (needed for creating elements in the correct character set and such) and the data array to the generate_xml_element() function. This function will create the top level element and then recursively call itself on any child elements until it’s gone through the whole array.

Once it’s done, you’ll need to append the element to the DOM using the inherited appendChild() function. Then calling saveXML will give you the XML output. The above example data array will give you something like this (maybe or maybe not nicely formatted with whitespace depending on what mood DOMDocument is in):

<?xml version="1.0"?>
<toplevelelement foo="bar" fruit="apple">
	Some random value.
	<achildelement>Value</achildelement>
	<anotherchildelement some="attr">
		<grandchildelement></grandchildelement>
	</anotherchildelement>
</toplevelelement>

And there you have it!

Creating Simple oEmbed-Based WordPress Shortcodes

Say you wanted to create a shortcode like this: [youtube id="991WcoEPwb8"]

And instead of manually creating the HTML yourself, you wanted to use YouTube’s oEmbed provider to get the HTML. While that’s easy to do using WordPress’ existing functions (wp_oembed_get() for example), you must implement your own caching of the result as WordPress’ oEmbed class does not include any caching of it’s own.

However, WordPress comes with an [embed] shortcode that’s also secretly used for the shortcode-less embeds. A cool trick is to make that shortcode’s existing code (complete with caching) work for you! This post explains how to do it.

First, the code:

add_shortcode( 'youtube', 'my_youtube_shortcode' );

function my_youtube_shortcode( $atts ) {

	// We need to use the WP_Embed class instance
	global $wp_embed;

	// The "id" parameter is required
	if ( empty($atts['id']) )
		return '';

	// Construct the YouTube URL
	$url = 'http://www.youtube.com/watch?v=' . $atts['id'];

	// Run the URL through the  handler.
	// This handler handles calling the oEmbed class
	// and more importantly will also do the caching!
	return $wp_embed->shortcode( $atts, $url );
}

We start by gaining access to the WP_Embed instance that we’ll be making use of and then making sure we have the required video ID (you can do whatever you want here). We then create the full URL to the video that oEmbed will need. Lastly the real time saver — we pass the attributes (“id” will be ignored) and the constructed URL to the [embed] shortcode handler which will fetch the result and cache it.

Simple, huh?

One thing to note though: if you’re using this along with a non-default oEmbed provider, you’ll also need to whitelist it using wp_oembed_add_provider().