The cost of honesty

There is a word in the English dictionary which I fear above all else. That word is cowardice. My fear is that when the time comes to do something gut-wrenchingly difficult I will stand back, lower my head and walk away.

Most people have the ability to absorb a huge amount of (for the lack of a better word) abuse. Verbal, emotional, and potentially, physical. People can lie to your face, you know it, but be too coward to confront them about it. You could witness injustice, but choose to turn a blind eye in fear of retribution. Be discriminated against, but not have the guts to say anything.

For a long time, I felt I had to fight for every little thing. But now, one big battle every couple of months makes up for all the minor ones I forfeit every day. Some lost battles are small. Getting cut off in traffic. Taking shit from a boss. The everyday slights that drive us up to the edge. Some lost battles are life altering. Losing someone you love. Failing to achieve the one thing you have worked hardest for. – Ronda Rousey

The price you pay for this belief, is knowing not everyone shares your views. Some people are cowards. Some people choose not to get involved, not to stand up for themselves, take the abuse and keep going. You could pick a battle where you find yourself alone. The people you are trying to help or protect could be too coward to back you up.

There’s nothing you can do about it, accept it and move on.

Practice makes perfect better.

The reason why I put myself through the things I hate doing is because I am callusing my mind. Im training for life so that when something tragic happens in my life, I don’t fall apart. I’m training my mind, my body, my spirit so its all one so that I can handle whatever life is going to throw at me. – David Goggins

Cowards lie. They lie because they can’t handle the truth, or the uncomfortable situation created by telling someone something they don’t want to hear. I’ve heard people say the truth hurts, but so does deceit and lies.

Practice what you preach.

Backup your QNAP TS-231P to Amazon S3

I recently had the good fortune of getting to play around with the setup of a QNAP TS-231P NAS drive. It’s really an amazing little home or office NAS drive. The primary reason we chose it was because you could backup your NAS to Amazon S3. This was a feature we desperately required as our old office NAS gave up the ghost and took all our backups along with it.

To make a long story short, all you do is install S3 Plus on the NAS’s operating system. The setup is quite simple, create an Amazon user, get the key and key secret, fill it into the wizard on S3 Plus, all of that works fine. Then you get to creating a backup job and you have to select an S3 bucket, only select the S3 bucket, but do not select a folder on S3, let the backup software create the folder for you, otherwise it will not work. It took me pretty much a day of Googling to figure this out.

I hope this saves some other poor I.T guy a few hours of his life.

Accessing WordPress wp-admin without logging in

The title might be a bit misleading. I don’t want to show you how it’s done, but rather how to fix it if it’s already happening.

I started noticing something strange happening with my blog. If I’m logged in on my PC, and I access wp-admin on my mobile phone, I am logged straight into wp-admin without having to authenticate myself. That’s a big problem.

I spent what felt like hours debugging this issue, switching W3TC off, but still it felt like some sort of caching issue. I run my DNS through CloudFlare and found that when I switch CloudFlare into development mode, the problem goes away.

I had a look at my CloudFlare Page Rules and found that I needed to do the following :

Browser Cache TTL: a day, Cache Level: Cache Everything, Edge Cache TTL: a month
Security Level: High, Cache Level: Bypass, Disable Apps, Disable Performance

The second page rule is the one you need to have, to tell CloudFlare to not cache the wp-admin.

That solved my problem.

Custom post type and taxonomy hierarchy in WordPress

For this blog post I’m going off the assumption you have a fairly good understanding of WordPress custom post types and taxonomies.

Let’s say you’re building a website for a company that provides a list of services. You could create a custom post type for services using the code below.

$args = array(
      'public' => true,
      'label'  => 'Services'
    register_post_type( 'service', $args );

Next thing you would want to do is create a standard WP page from where you will pull a list of all your custom post types and display them in a nice format for the user to make his/her selection to read up about that specific service by going to the service single page. You’ll likely create a page with the slug ‘/services/’ for this page.

By default the slug for your services custom post posts would be ‘/service/xxx’, which means when you’re on your ‘/services/’ page and you click on a single service your URL would change to ‘/service/xxx’ and for SEO that’s really bad.

A possible solution to this could be to rewrite your custom post type slug. You could do this as follows:

$args = array(
      'public' => true,
      'label'  => 'Services'
      'rewrite' => array('slug' => 'services'),
    register_post_type( 'service', $args );

Now both slugs are the same, but you face yet another problem. If you click back to the base page on your page breadcrumbs for example, you won’t ever see your standard WP page you created, you’ll see your custom post archive page. The ‘/services/’ base page is an archive page, not a WP page.

That you fix by doing this:

 $args = array(
      'public' => true,
      'label'  => 'Services'
      'rewrite' => array('slug' => 'services'),
      'has_archive' => FALSE,
    register_post_type( 'service', $args );

You disable the archive page.


Now. Let’s say you have different types of services and you want to categorize those services. You would create a custom taxonomy like this:

			'label' => __( 'Services Categories' )

The problem once again is in that your slug structure would be ‘/service-categories/’ for your custom taxonomy. You solve this once again by rewriting the URL:

			'label' => __( 'Services Categories' ),
			'rewrite' => array( 'slug' => 'services' ),
			'hierarchical' => true,

Let me expand on my services example. Let’s say our company does plumbing and gas installations. We could create a taxonomy for ‘plumbing’. If I create a services custom post for ‘unblocking drains’ and tag it as ‘plumbing’. I would expect my URL for that page to be ‘/services/plumbing/unblocking-drains/’, but WordPress does not give you that by default. Your URL would just be ‘/services/unblocking-drains/’. To get your taxonomy in your URL structure you have to rewrite it once again. You’ll do that with a filter and a function:

add_filter('post_type_link', 'my_awesome_post_link', 1, 2);

function my_awesome_post_link($permalink, $id){
//I'm going to let you write this for yourself
//My code is very specific to the project I did this for and won't work for you.
//Essentially what you need is to create the URL structure to include your taxonomies in the URL

//for example:
//return home_url().'/services/plumbing/unblocking-drains/';

This function will then create your link and everywhere you call


it will return the full path including your taxonomy structure.

But this isn’t all, now that you’ve created the URL structure you need to tell WP how to interpret it. You need to create a rewrite rule for it.

add_filter('generate_rewrite_rules', 'my_rewrite_rules');
function my_rewrite_rules($wp_rewrite) {
//I'm going to let you write this for yourself
//My code is very specific to the project I did this for and won't work for you.
//You need to create a rewrite rule for each of your custom post types including the full URL structure

//for example:

$custom_rules = array();

//for each of your custom posts:
$custom_rules['^services/plumbing/unblocking-drains$'] = 'index.php?service=unblocking-drains';

$wp_rewrite->rules = $custom_rules + $wp_rewrite->rules;
return $wp_rewrite->rules;

What this does is when you give WordPress this URL ‘/services/plumbing/unblocking-drains/’ it converts it to ‘index.php?services=unblocking-drains’ which WordPress is able to understand and return the correct content.

This is what I did in a nutshell.

I (re-)learned a couple of valuable lessons:

  • Unit testing
    • Whenever you start something that seems like it’s going to be small, just a few functions, it always ends up being more. A few hours later you sit with a class with a bunch of methods. You’re going to wish you started writing unit tests in the beginning.
  • Test data
    • You’re going to need real-world test data. Lorem ipsum doesn’t mean anything. Sometimes having real-world test data shows you certain edge-cases you would not have thought of when using generic data.

WordPress automatic meta description generation

Sometimes you’re just too lazy to create proper meta descriptions for a post or page and having a good(ish) meta description to display in your SERP is rather important for SEO.

I recently had to write a plugin to generate meta descriptions for all post types. The theory is, when you request the page, I have an action that runs on the

add_action('wp_head', 'xxx');

action. It then checks if you have a meta description for that page (assuming you have Yoast SEO installed), and if not, generates one based on a few parameters and appends it to your


section in your markup.

I’ll give you a base to start off with, this is by no means a complete and final version that you should use on your site. There is a lot more validation that needs to be done, but it’s a start. For now, you can put this in your functions.php file.

The bit of code below will check if you have a Yoast SEO meta description, if not, it will take the first sentence it finds in your content and use that as a meta description. Meta descriptions are 160 characters in length, anything over that will be truncated by Google.

add_action('wp_head', 'my_generate_custom_meta');

function my_generate_custom_meta() {
	global $wp_query;
	$yoast_meta_key = '_yoast_wpseo_metadesc';
	$post_id = get_the_ID();
	$yoast_meta_desc = get_post_meta($post_id, $yoast_meta_key, true);

	if (empty($yoast_meta_desc)) {
		$meta_desc = '';
		$title = strip_tags($wp_query->queried_object->post_title);
		$content = strip_tags($wp_query->queried_object->post_content);

		if (!empty($title) && !empty($content)) {
			$first_sentence = substr($content, 0, strpos($content, '.'));
			$meta_desc = $title . '. ' . $first_sentence;
			update_post_meta($post_id, $yoast_meta_key, $meta_desc);
			echo sprintf('', $meta_desc);


I’ve always gone through life living very frugal. Saving where I can. Whilst my friends have been living it up, going on holiday to fancy destinations, going on wine tastings, and generally just having fun.  This never really bothered me.  I never spent money on buying the next biggest television or buying a new car every year, things like that never really appealed to me. I enjoy living comfortably and that was enough for me.

A while ago I got married and my wife and I spent the most amazing week in Mauritius. Having our every whim catered to. Amazing. This really made us think. We’re constantly saving up money to buy our own house, or to replace the worn tyres on our car, or to replace a broken appliance. We never really spend money on ourselves, on experiences. Mauritius was an awakening, it showed us that life should be enjoyed.

In 2017, this will be our mission, spend more money on experiences. Sure, we will still save a lot, for other things, but we will take some time, and money, and go do something we normally would not.

Life is short, live a little. #Experiences.

How to fix the target=”_blank” exploit

I was bored whilst waiting for a Git repo to clone so I decided to play around a little with a very old exploit (four or five months old), so I wasn’t expecting much. The exploit I’m talking about is the target=”_blank” one. What happens is that when you open a link in a new tab, you have access to window.opener.location and that is accessible across origins (CORS).

All you need on your site to test the exploit is :

  if (window.opener) { opener.location = ''; }

(Single left-) Click here : you’ll just see my site open in a new tab
(Single left-) Click here : and watch the original window change to my “you’re hacked page”

I was keen to test this on a few social media sites where I have links to my blog. I never expected Facebook to be susceptible to this exploit but it was one of the first that I found to be susceptible. On my profile’s about page, when you click on my website URL, it opens in a new tab and the old one redirects to my “you’re hacked page”. Crazy.

The possibilities with this is endless. You can setup a spoof page for any social network or site you want to steal users’ credentials for and once they click on your link on the real site, their logged in user will be redirected to your fake site where they will be logged out, asking them to log back in, and so you steal their login details.

I alerted Facebook via their bug bounty program explaining the whole situation. After about 10mins I went to check and they fixed the bug, but had given no response on my ticket I logged. At the time of writing this blog entry, it’s been a few days and I still have not received a response. I’m not expecting an award, just an acknowledgement that I helped them out would be great.

Anyway, if your blog or site is affected by this, contact me to help you sort it out.

WordPress Custom Post Type names restricted to max 20 characters

Ever spotted this error:

: register_post_type was called
. Post type names must be between 1 and 20 characters in length. Please see
Debugging in WordPress
for more information. (This message was added in version 4.2.) in
on line

Thrown here:


	if ( empty( $post_type ) || strlen( $post_type ) > 20 ) {
		_doing_it_wrong( __FUNCTION__, __( 'Post type names must be between 1 and 20 characters in length.' ), '4.2' );
		return new WP_Error( 'post_type_length_invalid', __( 'Post type names must be between 1 and 20 characters in length.' ) );

So It would appear that due to legacy issues, your custom post type cannot exceed 20 characters.

What are these “legacy issues” I speak of? The post_type field in the wp_posts table in the database where the type of post (your custom post type’s name) is saved, is a VARCHAR(20)!

Screen Shot 07-06-16 at 08.58 AM

I have no words.