Hello! Thanks for stopping by. Feel free to browse around and remember that if you have any questions, to get in contact.

Location based content in PHP using IP addresses →

Posted on April 30, 2012 in Development with 0 comments.

With the internet being a globally accessible resource, there may come a time when you want to display different content to your website visitors based on their location. For instance, if you are a consultant you may want to display a US dollar hourly rate to people viewing your site from the United States and a euro hourly rate to those who are viewing your site from Europe. Doing this in PHP is rather easy, let’s look at how.

We’re going to base our script on IP addresses. It’ll involve looking up the visitors IP address and then matching it against a set of IP addresses specific to a country. Based on that we can easily show our visitor content that is locally relevant to them.

For the sake of simpleness, let’s build our application to detect if a visitor is from South Africa and then display a relevant message to them, otherwise we’ll display a generic message.

To start off, we need to build an array of IP ranges to match against. This method isn’t 100% fool-proof and in a real life application, if you are dealing with multiple countries or even provinces, I’d recommend that you use a location matching library like DeviceAtlas. But for a small application like ours, an array is perfect.

Here is the array with the IP address ranges for South Africa:

<?php
$ranges = array(
	array( '41.0.0.0', '41.31.255.255' ),
	array( '41.75.224.0', '41.75.239.255' ),
	array( '41.112.0.0', '41.127.255.255' ),
	array( '41.144.0.0', '41.151.255.255' ),
	array( '41.154.0.0', '41.154.255.255' ),
	array( '41.156.0.0', '41.157.255.255' ),
	array( '41.160.0.0', '41.175.255.255' ),
	array( '41.177.0.0', '41.177.255.255' ),
	array( '41.180.0.0', '41.180.255.255' ),
	array( '41.181.0.0', '41.181.255.255' ),
	array( '41.183.0.0', '41.183.255.255' ),
	array( '41.185.0.0', '41.185.255.255' ),
	array( '41.188.192.0', '41.188.255.255' ),
	array( '41.189.64.0', '41.189.95.255' ),
	array( '41.191.128.0', '41.191.191.255' ),
	array( '41.192.0.0', '41.192.255.255' ),
	array( '41.193.0.0', '41.193.255.255' ),
	array( '41.194.0.0', '41.194.255.255' ),
	array( '41.195.0.0', '41.195.255.255' ),
	array( '41.198.0.0', '41.198.255.255' ),
	array( '41.202.32.0', '41.202.63.255' ),
	array( '41.203.0.0', '41.203.31.255' ),
	array( '41.203.32.0', '41.203.63.255' ),
	array( '41.203.160.0', '41.203.175.255' ),
	array( '41.204.192.0', '41.204.223.255' ),
	array( '41.206.160.0', '41.206.191.255' ),
	array( '41.206.192.0', '41.206.223.255' ),
	array( '41.207.224.0', '41.207.255.255' ),
	array( '41.208.0.0', '41.208.63.255' ),
	array( '41.208.192.0', '41.208.255.255' ),
	array( '41.213.0.0', '41.213.127.255' ),
	array( '41.216.128.0', '41.216.143.255' ),
	array( '41.216.192.0', '41.216.207.255' ),
	array( '41.221.0.0', '41.221.15.255' ),
	array( '41.221.224.0', '41.221.239.255' ),
	array( '41.240.0.0', '41.247.255.255' ),
	array( '66.8.0.0', '66.8.127.255' ),
	array( '66.18.64.0', '66.18.95.255' ),
	array( '69.67.32.0', '69.67.47.255' ),
	array( '137.158.0.0', '137.158.255.255' ),
	array( '137.214.0.0', '137.214.255.255' ),
	array( '137.215.0.0', '137.215.255.255' ),
	array( '139.53.0.0', '139.53.255.255' ),
	array( '143.128.0.0', '143.128.255.255' ),
	array( '143.160.0.0', '143.160.255.255' ),
	array( '146.64.0.0', '146.64.255.255' ),
	array( '146.141.0.0', '146.141.255.255' ),
	array( '146.182.0.0', '146.182.255.255' ),
	array( '146.230.0.0', '146.230.255.255' ),
	array( '146.231.0.0', '146.231.255.255' ),
	array( '146.232.0.0', '146.232.255.255' ),
	array( '147.110.0.0', '147.110.255.255' ),
	array( '152.106.0.0', '152.106.255.255' ),
	array( '152.107.0.0', '152.107.255.255' ),
	array( '152.108.0.0', '152.108.255.255' ),
	array( '152.109.0.0', '152.109.255.255' ),
	array( '152.110.0.0', '152.110.255.255' ),
	array( '152.111.0.0', '152.111.255.255' ),
	array( '152.112.0.0', '152.112.255.255' ),
	array( '155.159.0.0', '155.159.255.255' ),
	array( '155.232.0.0', '155.232.255.255' ),
	array( '155.233.0.0', '155.233.255.255' ),
	array( '155.234.0.0', '155.234.255.255' ),
	array( '155.235.0.0', '155.235.255.255' ),
	array( '155.236.0.0', '155.236.255.255' ),
	array( '155.237.0.0', '155.237.255.255' ),
	array( '155.238.0.0', '155.238.255.255' ),
	array( '155.239.0.0', '155.239.255.255' ),
	array( '155.240.0.0', '155.240.255.255' ),
	array( '156.8.0.0', '156.8.255.255' ),
	array( '160.115.0.0', '160.115.255.255' ),
	array( '160.116.0.0', '160.116.255.255' ),
	array( '160.117.0.0', '160.117.255.255' ),
	array( '160.118.0.0', '160.118.255.255' ),
	array( '160.121.0.0', '160.121.255.255' ),
	array( '160.122.0.0', '160.122.255.255' ),
	array( '160.123.0.0', '160.123.255.255' ),
	array( '160.124.0.0', '160.124.255.255' ),
	array( '163.195.0.0', '163.195.255.255' ),
	array( '163.196.0.0', '163.196.255.255' ),
	array( '163.197.0.0', '163.197.255.255' ),
	array( '163.198.0.0', '163.198.255.255' ),
	array( '163.199.0.0', '163.199.255.255' ),
	array( '163.200.0.0', '163.200.255.255' ),
	array( '163.201.0.0', '163.201.255.255' ),
	array( '163.202.0.0', '163.202.255.255' ),
	array( '163.203.0.0', '163.203.255.255' ),
	array( '164.88.0.0', '164.88.255.255' ),
	array( '164.146.0.0', '164.151.255.255' ),
	array( '164.155.0.0', '164.155.255.255' ),
	array( '165.3.0.0', '165.5.255.255' ),
	array( '165.8.0.0', '165.11.255.255' ),
	array( '165.25.0.0', '165.25.255.255' ),
	array( '165.143.0.0', '165.149.255.255' ),
	array( '165.165.0.0', '165.165.255.255' ),
	array( '165.180.0.0', '165.180.255.255' ),
	array( '165.233.0.0', '165.233.255.255' ),
	array( '166.85.0.0', '166.85.255.255' ),
	array( '168.76.0.0', '168.76.255.255' ),
	array( '168.80.0.0', '168.81.255.255' ),
	array( '168.89.0.0', '168.89.255.255' ),
	array( '168.128.0.0', '168.128.255.255' ),
	array( '168.142.0.0', '168.142.255.255' ),
	array( '168.155.0.0', '168.155.255.255' ),
	array( '168.164.0.0', '168.164.255.255' ),
	array( '168.172.0.0', '168.172.255.255' ),
	array( '168.206.0.0', '168.206.255.255' ),
	array( '168.209.0.0', '168.210.255.255' ),
	array( '169.129.0.0', '169.129.255.255' ),
	array( '169.202.0.0', '169.202.255.255' ),
	array( '196.1.32.0', '196.1.51.255' ),
	array( '196.1.144.0', '196.1.159.255' ),
	array( '196.2.16.0', '196.2.31.255' ),
	array( '196.2.32.0', '196.2.63.255' ),
	array( '196.2.64.0', '196.2.79.255' ),
	array( '196.2.96.0', '196.2.127.255' ),
	array( '196.2.128.0', '196.2.159.255' ),
	array( '196.2.160.0', '196.2.191.255' ),
	array( '196.3.164.0', '196.3.179.255' ),
	array( '196.3.224.0', '196.3.255.255' ),
	array( '196.4.0.0', '196.4.19.255' ),
	array( '196.4.100.0', '196.4.149.255' ),
	array( '196.4.173.0', '196.4.188.255' ),
	array( '196.4.212.0', '196.4.231.255' ),
	array( '196.5.0.0', '196.5.255.255' ),
	array( '196.6.1.0', '196.6.100.255' ),
	array( '196.6.133.0', '196.6.172.255' ),
	array( '196.7.0.0', '196.7.255.255' ),
	array( '196.8.0.0', '196.8.255.255' ),
	array( '196.9.0.0', '196.9.255.255' ),
	array( '196.10.1.0', '196.10.50.255' ),
	array( '196.10.61.0', '196.10.95.255' ),
	array( '196.10.150.0', '196.10.199.255' ),
	array( '196.11.0.0', '196.11.30.255' ),
	array( '196.12.16.0', '196.12.31.255' ),
	array( '196.13.1.0', '196.13.30.255' ),
	array( '196.13.31.0', '196.13.80.255' ),
	array( '196.13.81.0', '196.13.100.255' ),
	array( '196.14.0.0', '196.14.255.255' ),
	array( '196.15.64.0', '196.15.127.255' ),
	array( '196.15.128.0', '196.15.255.255' ),
	array( '196.16.0.0', '196.19.255.255' ),
	array( '196.21.0.0', '196.21.255.255' ),
	array( '196.22.16.0', '196.22.31.255' ),
	array( '196.22.32.0', '196.22.47.255' ),
	array( '196.22.64.0', '196.22.127.255' ),
	array( '196.22.160.0', '196.22.191.255' ),
	array( '196.22.192.0', '196.22.239.255' ),
	array( '196.22.240.0', '196.22.255.255' ),
	array( '196.23.0.0', '196.23.255.255' ),
	array( '196.24.0.0', '196.24.255.255' ),
	array( '196.25.0.0', '196.25.255.255' ),
	array( '196.26.0.0', '196.26.255.255' ),
	array( '196.28.16.0', '196.28.47.255' ),
	array( '196.28.64.0', '196.28.127.255' ),
	array( '196.28.128.0', '196.28.223.255' ),
	array( '196.29.0.0', '196.29.31.255' ),
	array( '196.29.128.0', '196.29.159.255' ),
	array( '196.29.240.0', '196.29.255.255' ),
	array( '196.30.0.0', '196.30.255.255' ),
	array( '196.31.0.0', '196.31.255.255' ),
	array( '196.32.160.0', '196.32.191.255' ),
	array( '196.33.0.0', '196.33.255.255' ),
	array( '196.34.0.0', '196.35.255.255' ),
	array( '196.36.0.0', '196.39.255.255' ),
	array( '196.40.96.0', '196.40.111.255' ),
	array( '196.41.0.0', '196.41.31.255' ),
	array( '196.41.96.0', '196.41.127.255' ),
	array( '196.41.128.0', '196.41.159.255' ),
	array( '196.41.160.0', '196.41.191.255' ),
	array( '196.41.192.0', '196.41.223.255' ),
	array( '196.43.0.0', '196.43.63.255' ),
	array( '196.44.0.0', '196.44.31.255' ),
	array( '196.44.32.0', '196.44.47.255' ),
	array( '196.44.64.0', '196.44.95.255' ),
	array( '196.44.192.0', '196.44.207.255' ),
	array( '196.44.208.0', '196.44.223.255' ),
	array( '196.44.224.0', '196.44.239.255' ),
	array( '196.45.16.0', '196.45.31.255' ),
	array( '196.45.64.0', '196.45.95.255' ),
	array( '196.45.96.0', '196.45.111.255' ),
	array( '196.46.160.0', '196.46.175.255' ),
	array( '196.47.0.0', '196.47.63.255' ),
	array( '196.47.64.0', '196.47.95.255' ),
	array( '196.207.32.0', '196.207.47.255' ),
	array( '196.208.0.0', '196.211.255.255' ),
	array( '196.212.0.0', '196.215.255.255' ),
	array( '196.220.32.0', '196.220.63.255' ),
	array( '198.54.22.0', '198.54.37.255' ),
	array( '204.12.128.0', '204.12.143.255' ),
	array( '209.203.0.0', '209.203.63.255' ),
	array( '209.212.96.0', '209.212.127.255' ),
	array( '213.193.32.0', '213.193.63.255' ),
	array( '216.236.176.0', '216.236.191.255' )
);
?php>

Before we go any further, we need to write a function to check if the IP address we have got from the visitors user agent is valid.

<?php
function checkValidIp( $ip ) {
	$invalid = array(
		array( '10.0.0.0', '10.255.255.255' ),
		array( '127.0.0.0', '127.255.255.255' ),
		array( '169.254.0.0', '169.254.255.255' ),
		array( '172.16.0.0', '172.31.255.255' ),
		array( '192.0.2.0', '192.0.2.255' ),
		array( '192.168.0.0', '192.168.255.255' ),
		array( '255.255.255.0', '255.255.255.255' )
	);

	$ip = ip2long( $ip );

	foreach( $invalid as $ips ) {
		if ( ip2long( $ips[0] ) <= $ip && $ip <= ip2long( $ips[1] ) )
			return false;
	}

	return true;
}
?php>

As you can see, we accept an IP address in our checkValidIP function. We then setup an array of invalid IP address (which are mostly local IP address ranges), run through them and if we find a match we return false i.e. it is not a valid IP address.

You will have noticed one important PHP function that was used in the code snippet. That is the ip2long function. It takes an IP address and converts it to the type of a long integer. We can then easily check if the IP address falls within the invalid range. We're going to use this function again below.

The next step is to fetch the IP address of the visitor and match it against our ranges array. Here is a small function that does exactly that.

<?php
function isSouthAfrican() {
	// Range array goes here
	$ranges = array()

	if ( checkValidIp( $_SERVER['HTTP_CLIENT_IP'] ) ) {
		$ip = $_SERVER['HTTP_CLIENT_IP'];
	}

	if ( ! isset( $ip ) ) {
		if ( $ips = count( explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) > 0 ) {
			foreach ( $ips as $i ) {
				if ( checkValidIp( trim( $i ) ) ) {
					$ip = $i;
					break;
				}
			}
		}
	}

	if ( ! isset( $ip ) ) {
		if ( checkValidIp( $_SERVER['HTTP_X_FORWARDED'] ) ) {
			$ip = $_SERVER['HTTP_X_FORWARDED'];
		} else if ( checkValidIp( $_SERVER['HTTP_FORWARDED_FOR'] ) ) {
			$ip = $_SERVER['HTTP_FORWARDED_FOR'];
		} else if ( checkValidIp( $_SERVER['HTTP_FORWARDED'] ) ) {
			$ip = $_SERVER['HTTP_FORWARDED'];
		} else if ( checkValidIp( $_SERVER['HTTP_X_FORWARDED'] ) ) {
			$ip = $_SERVER['HTTP_X_FORWARDED'];
		} else {
			$ip = $_SERVER['REMOTE_ADDR'];
		}
	}

	$ip = ip2long( $ip );

	foreach ( $ranges as $range ) {
		if ( ip2long( $range[0] ) <= $ip && $ip <= ip2long( $range[1] ) )
			return true;
	}

	return false;
}
?php>

In this snippet we check numerous user agent variables for the IP address to try and bypass proxies and weird browser behavior. Finally we run through our IP ranges and see if we get a match. If so, we naturally return true. Include this code in your project and then simply call the isSouthAfrican function and voila!

<?php
if ( isSouthAfrican() ) {
	echo "We're in South Africa baby!";
} else {
	echo "Hello world";
}
?php>

To test this code out, you'll need to use a proxy. Just do a Google search and you'll find thousands of international proxies that you can use. Change the IP ranges based on the country you want to check against.

You can download a full working version of the code here.

Tagged with , , ,

I’m going to be a Dad! →

Posted on April 29, 2012 in Life with 1 comment.

I’ve got some really exciting news to share. Megg is 12 weeks pregnant and in November of this year our family is going to be plus one. We are so excited and cannot wait for the baby to arrive. For now though, it’s a whole lot of planning and shopping in preparation for the arrival.

Here is a picture from the 12 week scan. What do you think, boy or girl?

Baby Geri

Megg also did a post with some more pictures. Check it out.

Tagged with

I’ve joined Tutuka! →

Posted on April 10, 2012 in Life with 0 comments.

It’s been a little while since I have blogged and a lot has been happening in my life. One of the big so called happenings that I am really excited to share with you today, is that I have now taken on a full time job at Tutuka. Since April 1st, I am officially a “Tutukan”.

Tutuka is the leading prepaid card and voucher company in South Africa. You will most likely not have ever heard of the company, but will more than likely have used one of their products. Have you ever bought a card based gift voucher for a friend or a gift card from a shopping mall in South Africa? Well then you’ve most likely used a Tutuka product.

Personally, it was a tough choice to leave the full time freelance world and move back in to full time employment. The freelance lifestyle is nice, make your own hours and be your own boss but it definitely comes as a cost. More specifically, I was working crazy hours and had some unreliable clients. Not to mention spending most of my time in meetings and not actually coding. I weighed up the pro’s and con’s and decided at this time of my life, it definitely makes sense to be full time employed.

Another motivation for me to go full time was reading the book called “Quitter” by Jon Acuff. I highly recommend it as one of my top reads so far this year.

So, after interviewing with a few companies and chatting to a number of people, Tutuka was my number 1 choice. It’s a great company, with very interesting code challenges and a good vibe. Not to mention free drinks and an awesome laptop! I was very grateful that I got an offer from them.

On the topic of code challenges, as you most likely know from my blog, I come from a PHP/MySQL background and at Tutuka I am coding in Coldfusion. It’s an interesting language that I have so far just touched the surface of and I am looking forward to using it more in the future. Subsequently, you will be seeing a lot more Coldfusion posts on my blog in the future ;)

All in all, I am very happy so far at Tutuka and I am looking forward to hopefully many happy years working for them.

Tagged with , ,

Blog design tip: add a link back to your websites homepage →

Posted on February 23, 2012 in Development with 0 comments.

This is more of a short rant with a tip at the end of it ;)

There is nothing more annoying than visiting a blog, be it via Google, a twitter link or however you end up on the blog and browsing the blog and then deciding to view the company/product home page, only to find that you have to physically retype the URL bar and/or remove the ‘blog.’ or ‘/blog/’ piece to get to the home page.

I’ve noticed quite a number of popular blogs that don’t have a clear link back to the homepage. Some will have a small disguised link (usually on the sidebar of the blog) that is very difficult to find. Why make it so difficult for a visitor and/or potential customer to go to your home page.

This practice usually occurs a lot on 3rd party hosted blogs like WordPress.com, Blogger, Posterous, etc. I understand that it can sometimes be tricky to edit the blog theme and add that link, but it is vitally important. Pay someone to do it for you.

I’ve actually started to close the browser tab on a blog who does not link back to the homepage, even if I am interested in the product or service!

So take 5 minutes to quickly add a link back to your homepage.

Tagged with

Introducing my latest WordPress Plugin – WP Most Popular →

Posted on February 13, 2012 in Projects with 2 comments.

I recently wrote a plugin for WordPress called WP Most Popular and forgot to announce it on my blog. WP Most Popular is a very simple plugin that aims to excel at one simple task and that is, to give you an accurate indication of your most popular / most read blog posts.

The plugin was launched just over a week or so ago and has already clocked over 1000 downloads!

The plugin comes with a sidebar widget which allows you to easily add a list of your most popular blog posts to any widget enabled WordPress theme and as well as that, it comes with a custom function which lets you display the most popular posts anywhere on your theme with tons of flexibility!

I would only recommend that small to medium traffic websites use the plugin as it is very write heavy. I am hoping to improve on this in the future as well as add a few other features that I have in mind for it.

Obviously, the plugin is open source and if you want to contribute, fix a bug or add a new feature, then you are more than welcome to. It is hosted on my Github profile.

I have setup a project page on this website for WP Most Popular and it is also hosted in the official WordPress plugin directory.

Tagged with , ,

How to add the TinyMCE editor to a WordPress meta box or plugin form →

Posted on February 7, 2012 in Development with 0 comments.

I’ve worked on a number of WordPress sites and custom plugins recently where I have needed to add the TinyMCE editor to a meta box that was added to a post type and/or a custom textarea fields in a plugin. Pre-WordPress 3.0 this was quite difficult and required a bit of hacking, but luckily since WordPress version 3.0 they have made it extremely easy.

In WordPress 3.0 a new function was introduced called ‘wp_editor()‘. This function allows us to easily add a the TinyMCE editor to our meta box or plugin.

The wp_editor() function gives us quite a bit of flexibility through its parameters. Let’s take a quick look at an example of adding an editor to a meta box. I’m going to skip out the part where we define the meta box and skip directly to the function which outputs our meta box content.

<?php
function display_meta_box_content( $post ) {
	echo '<label for="content">Content</label>';
	wp_editor( '', 'content-id', array( 'textarea_name' => 'content', 'media_buttons' => false ) );
}
?>

Simple as that. You can also use this in your plugin. Just insert the wp_editor() function where you want a textarea to display on your admin page.

Check out the wp_editor() page in the WordPress codex for extra parameters you can pass the function. You can also set TinyMCE settings using the ‘tinymce’ parameter. For instance, let’s look at a quick example where we define what formatting options we want displayed and add a spellchecker to the TinyMCE editor.

<?php
	wp_editor( '', 'content-id', array( 'textarea_name' => 'content', 'media_buttons' => false, 'tinymce' => array( 'theme_advanced_buttons1' => 'formatselect,forecolor,|,bold,italic,underline,|,bullist,numlist,blockquote,|,justifyleft,justifycenter,justifyright,justifyfull,|,link,unlink,|,spellchecker,wp_fullscreen,wp_adv' ) ) );
?>

Just one small thing!

Unfortunately, the editor cannot be moved around the DOM which means the editor will not work if you move the meta box. There is discussion around this on trac where a few proposed solutions are put forward, such as static meta boxes that cannot be dragged, but as it currently stands, there is no work around for this.

This only effects meta boxes though and not plugins.

Tagged with , ,

Quick tip for WordPress developers: Use the WP_DEBUG constant →

Posted on February 3, 2012 in Development with 0 comments.

Over the past few years I have worked with many different WordPress plugins and themes, both on my own websites and on my clients websites. During this time, I have noticed something that a lot of developers are doing that they shouldn’t be doing. I’m talking about developers not using the WP_DEBUG constant when building their plugins and themes.

I was guilty of this before as well. I never used it mostly due to ignorance and not knowing what it does. I discovered it when I was working on a client website who had it set to true in their live environment (not a good idea) and their website was throwing tons of PHP errors.

What does the WP_DEBUG constant do?

WP_DEBUG is a simple flag that tells WordPress you want to debug your code and that PHP errors should be displayed. If it is set to false, WordPress will suppress many of those PHP errors (such as undefined index’s) and you will never know that they exist – That is until you look at your web server logs and see all the errors showing up.

The WP_DEBUG constant can also be very useful. Let’s use analytics as an example. In your development environment you do not want to track analytics. Debug should always be set to true in development environments so lets assume that it is. To stop the analytics script from collecting data (running the analytics script), you can use this code:

<?php if ( ! WP_DEBUG ): ?>
// Display analytics code in your theme
<?php endif; ?>

If you constantly turn the WP_DEBUG flag on and off in your development environment, then I recommend creating a new constant called WP_DEV in your wp-config.php file to get the above code to work.

You’d be very surprised to see how many of the popular plugins actually have basic PHP errors in them such as undefined indexes. Install a couple plugins, turn the WP_DEBUG flag on and see for yourself.

As mentioned above, you should set WP_DEBUG to false in any live environment.

Tagged with

Symbolic links on a Mac (OS X) →

Posted on January 23, 2012 in Development with 0 comments.

If you are a developer and have not come across symbolic links before, then you need to read this blog post. Symbolic links are extremely useful if you hate copying and pasting duplicate files and folders in different places/projects.

So what exactly are they? Simply speaking, they are a virtual link or shortcut between a file or folder from a different file/folder location.

So for instance, I have a project that I am working on in a directory called ‘Example-Project’ that lives in a ‘Projects’ directory. Let’s say that this is a WordPress site. Now, I also have a plugin that I have written called ‘Display-Latest-Posts’ and this plugin also sits in the main ‘Projects’ directory. Now, I want to use this plugin on the Example-Project site.

There are two ways in which we can do this

  1. Copy/Paste the plugin files from ‘Display-Latest-Posts’ into ‘Projects/Example-Project/wp-content/plugins/’
  2. Create a symbolic link

The problem with method number 1 above, is that if I change some code in the ‘Display-Latest-Posts’ plugin, I have to go and copy/paste the files I changed back into the ‘Example-Project’ folder. Now imagine that you have the plugin running on a number of sites in your development environment. That could take a while.

Introducing symbolic links

Symbolic links solve the problem that I just mentioned. When you create a symbolic link in a folder, any changes to the files in the original folder will be reflected in the linked folder, since the linked folder is actually just a reference/shortcut of the original folder. It is not two separate folders.

To create a symbolic link, open terminal and type the following:

ln -s /Path/to/original/folder New-folder-name

So in our example above, we would have created the following symbolic link:

ln -s /Projects/Display-Latest-Posts /Projects/Example-Project/wp-content/plugins/Display-Latest-Posts

The symbolic link is now created between those two folders and our plugin will now be working in our Example-Project site.

Symbolic links are really easy to use and can save you tons of time. Give them a try.

Tagged with

How to ignore WordPress pagination and display all posts on a page →

Posted on January 19, 2012 in Development with 2 comments.

There are times when you want to display a list of WordPress posts (or even custom post types) on a page and not have them paginated into multiple different WordPress pages. For example, you may be creating an archive page and instead of showing just the latest 10 posts in your category, you want to show the whole list of posts in that category.

I recently come across this challenge when building the theme for this site and it is actually very easily overcome. You just need to add a little bit of PHP code to your theme file.

Let’s assume we are working on an archive page (archive.php in WordPress). In that file you have your standard WordPress loop which will display the latest 10 posts (or however many you have set to display per page in your WordPress settings). So we have something like this…


<php
if ( have_posts() ) : while ( have_posts() ) : the_post();
	// Your code here
endwhile; endif;
php>

Now, to avoid showing just the latest 10 posts, we need to do this


<php
global $query_string;
query_posts( $query_string . '&posts_per_page=-1' );
if ( have_posts() ) : while ( have_posts() ) : the_post();
	// Your code here
endwhile; endif;
wp_reset_query();
php>

Simple enough. All we added was 3 extra lines of code.

Firstly, we reference the $query_string global so that we can use it in the code. Then we use the existing query string that was set to fetch the posts and add an extra parameter to the end of the query string called “posts_per_page” to display a custom number of posts, in this case we use -1 to display all posts. And lastly, we reset the query after we are done.

Tagged with

It’s been a long time coming →

Posted on January 16, 2012 in General with 2 comments.

It can safely be said, that for the first time in history I have a personal website that doesn’t have a section which displays a “coming soon” sign on it. This website has been through many iterations in the past and every time it has never been complete.

I’ve been working extremely hard over the past week and a bit to revamp the sites theme and complete all the sub page sections on the top menu. Finally, about 10 minutes ago I pushed the last changes.

I am very happy with the outcome. I’ve added sections where I can show off some of my work, link to articles I write, share my talks and lots more. Most importantly, since I am back in the freelance game (more on this later), I have added a section called “Consulting”. In this section you will find a list of services that I offer as a freelancer and consultant, testimonials from previous clients and also some of my client portfolio.

There are sections of the site that I want to improve on and I have still got to add quite a few projects I’ve worked on and articles that I have written, but that will only happen throughout the year.

I’m planning on blogging a lot more this year and also writing many more technical articles (especially on other development websites). My plan is to keep this site fresh and up to date with what’s happening in my life and development life.

Stay tuned.

Tagged with , , ,