Monthly Archives: May 2014

Last updated by at .

WordPress Posts with Identical Dates

I’m building a catalog site in WordPress for one of my collections. The catalog currently exists in a large Excel spreadsheet and I’ve been usng it to create a CSV file that can be imported using the WP CSV plugin. This greatly speeds up the creation of the content for the catalog. My plan is that each item in my catalog is a post and the CSV file contains all of the information for each post. One of the imported values in the CSV file includes is the post date. I imported each item with an identical post date without giving it too much thought. This is because the catalog site isn’t going to actually display any post dates. Anyway, it turns out that this was a bad idea because the Next Post and Previous Post links in the single.php template will not bring up any of the posts with the same post date. A bit of digging about and I’ve worked out why.

My single.php theme file contains calls to previous_post_link() and next_post_link() which are supposed to render the next/previous links. I drilled down into these functions which can be found in the /wp-includes/link-template.php file. Both functions call the function get_adjecent_post(). Looking at that a bit closer it includes a bunch of SQL criteria code (for selecting posts from the same taxonomy for example) and, as you’d expect it creates a date criteria clause. That bit of php looks like this:

$adjacent = $previous ? 'previous' : 'next';
 $op = $previous ? '<' : '>';
 $order = $previous ? 'DESC' : 'ASC';

Those little ‘<' and '>‘ symbols give the answer why my imported posts with identical post dates don’t display in the next/previous post links. The date criteria to select the next/previous post is “greater than” or “less than” and obviously if the post dates are identical they are NOT greater than or less than. So, the solution is to NOT import posts with identical post dates. Unfortunately I’d already done this so I opened up the wp_posts table in MySQL workbench and edited the post date of each post I’d imported. Next time I do a bulk import I’ll make sure to increment the post date column values in the CSV BEFORE I do the import.

Sorting WordPress Category Archives

By default WordPress category archives are sorted by date.  This is fine when you’re publishing a blog but if you’re using WP for other reasons you might want to adjust the way the archives are sorted.  This is done rather easily by creating a new filter in your functions.php file like this:

//filter to alphabetically sort category archives

And then creating a new sort function like this:

function sort_category_archive_alphabetically($orderby )
 if (is_category())
 return "post_title ASC";
 return $orderby;

In this example I’m alphabetically sorting all categories.  You could restrict it to a specific category by changing the is_category() call to include a category ID (is_category(8)) or by including a category name (is_category(‘Your Category Name’)).

I’m doing a simple sort by post_title here but more complex sorts are possible.  For example, the WordPress API gives a more complex example that explains how to do a LEFT JOIN in the SQL to select posts and sort by values in tables other than the WP_POSTS table.

Default Image Captions in WordPress

I’m building a numismatic reference website in WordPress for the Perth Numismatic Society that is eventually going to list several thousand items along with images. The images will be presented as thumbnails which can be clicked on to show larger images using the Responsive Lightbox plugin. The client wanted a default caption under each image instructing users to click on the image to view a larger one. I honestly thought adding a default image caption would be easy but it turns out it isn’t. Nothing I Googled up actually worked including this example in the WordPress Codex or this plugin. So I actually had to use the old grey matter and work it out myself.

So I had a poke around the and ended up in the /wp-admin/includes/media.php file and found this function and filter:

function image_add_caption( $html, $id, $caption, $title, $align, $url, $size, $alt = '' )
//function code removed
add_filter( 'image_send_to_editor', 'image_add_caption', 20, 8 );

Ahah, simple. Remove the existing image_add_caption filter and replace it with my own. So, I’ve grabbed that code and added it and a couple of other things to my child theme functions.php file as:

remove_filter( 'image_send_to_editor', 'image_add_caption');
add_filter( 'image_send_to_editor', 'add_default_caption', 100, 8);
function add_default_caption( $html, $id, $caption, $title, $align, $url, $size, $alt = '' ) {

	 * Filter whether to disable captions.
	 * Prevents image captions from being appended to image HTML when inserted into the editor.
	 * @since 2.6.0
	 * @param bool $bool Whether to disable appending captions. Returning true to the filter
	 *                   will disable captions. Default empty string.
        if (substr($html,0,strlen('([0-9]+)/', $html, $matches ) )
		return $html;

	$width = $matches[1];

	$caption = str_replace( array("rn", "r"), "n", $caption);
	$caption = preg_replace_callback( '/<[a-zA-Z0-9]+(?: [^<>]+>)*/', '_cleanup_image_add_caption', $caption );
	// convert any remaining line breaks to <br>
	$caption = preg_replace( '/[ nt]*n[ t]*/', '<br />', $caption );

	$html = preg_replace( '/(class=["'][^'"]*)align(none|left|right|center)s?/', '$1', $html );
	if ( empty($align) )
		$align = 'none';

	$shcode = '' . $html . ' ' . $caption . '';

	 * Filter the image HTML markup including the caption shortcode.
	 * @since 2.6.0
	 * @param string $shcode The image HTML markup with caption shortcode.
	 * @param string $html   The image HTML markup.
	return apply_filters( 'image_add_caption_shortcode', $shcode, $html );

You can see I start by removing the existing image_add_caption filter but it turns out this is an utter waste of time because the filter executes even though I’ve removed it. So I’ve got around this by declaring the new filter with a lower priority than the existing one (so it happens AFTER) the existing filter. And then checked the $html to see if it starts with the caption shortcode. That’s this line:

if (substr($html,0,strlen('[caption'))=='[caption')
 return $html;

So, if a caption has already been applied we just exit from our new filter.  However, if there’s no caption we can now apply our default caption like this:

if (empty($caption))
 $caption="Click Image to Enlarge";

Uninstall Surveys

There was a post on the almost dead Business of Software forum last week from a poster who had recently implemented an ‘uninstall survey’ in his try-before-you-buy software product. An uninstall survey is triggered when a software product is uninstalled from the host operating system. It usually takes one of two forms, the first is a small program that asks some questions and then emails the results or posts them to a website. The second (and far more common) type of survey is triggered by popping up a webpage on the software product website.

But, I digress, the main point of the post was that the number of responses the poster was receiving was small and the number of useful responses was even smaller. I’ve included an uninstall survey with my product Time Clock MTS for several years and this mirrors my experience. I’m lucky to see a 10% response rate (10% of total downloads) and of those, not more than 1 in 10 or 20 actually contains useful information. At first glance it seems to be hardly worth the effort but the hour or two it took to implement has probably seen an ROI measured in thousands of percent. It has allowed me to save the occasional sale by responding to a user who complained of missing features in my product that were not missing at all. It has allowed me to find holes in my documentation and fill them, it has highlighted certain software features I was missing and that respondents found useful, and it has helped me fix up problems with the process flow in the software.

My experience certainly pours water on the comments of one poster on the BoS forums who suggested:

Indirectly related, the existence of an uninstaller often implies a poor architecture, such as apps that stuff random files and overwrite libraries in many places.

This is an insanely negative attitude. Knowing that a product is not perfect and can be improved is the ONLY way of being able to build a better product. And the best way to build a better product is to establish channels of communication with your customers. The software uninstall survey is one channel that is virtually free to establish and provides a means of communication that, in the case of downloadable trial software, would otherwise be impossible. Consider that a trial version of my software can be downloaded, installed, and trialed all without the user having to email me, ask me for a registration key, or even make me aware that they are using the software. Giving the user the opportunity to communicate with me once they have decided to uninstall (and presumably not purchase) my software has proven to be invaluable.

If you’re wanting to implement an uninstall survey, you sell Windows software and you’re using the very nifty (and free) Inno Setup then it’s dead simple. Just add an URL to the [INI] section of your Inno Setup script like this:

Filename: {app}UninstallSurvey.url; Section: InternetShortcut; Key: URL; String:

And then add an [UninstallRun] section to your script that opens the URL.

Filename: {app}UninstallSurvey.url; Verb: open; Flags: shellexec

In my case I open an URL to present the user with questions. But there’s no reason why the [UninstallRun] section couldn’t run a small executable file that pops a window up to the user asking them the same questions. Those results could either then be emailed or POST’ed to your website. For your reference here’s the Time Clock MTS Uninstall Survey. It used to have a lot more questions but I’ve simplified it greatly based on the response of one poster in the BoS thread I referenced in the first paragraph. Respondents simply fill in the details and I receive an email contain the responses and a couple of other bits of information such as the users IP address and user-agent. The user-agent is particularly useful so that I know what version of Windows the respondent is using.

There you have it, uninstall surveys. Quick and easy to implement, zero on-going costs, and they provide you with a communication channel to software users that is unlikely to exist otherwise. If you’re selling try-before-you-buy software you’d be crazy to not be using them.