Setup redmine / artifactory / hudson on fedora

Just a quick run through of what I’ve been doing for the last few days, namely setting up our build / development server.  It’s running Fedora, and the end goal was to have redmine, artifactory and hudson all on the same server, all running on port 80.  Once it’s installed and running we should be able to run automated builds (using hudson) when we commit to subversion and have those builds available from maven (using artifactory) for our Servicemix installation on another server.

It’s been a fun ride, redmine in particular was loads of fun to get going since I’ve never really setup a rails application before.  Anyway, on with the run through.  Please note that this is a very basic guide, and assumes you’re comfortable with the linux command line and so on, it’s not really for beginners.


Install fedora, ensure apache / mysql are installed and working, also install the latest JDK (guide here)  and ensure your JAVA_HOME environment variable is set correctly.


Download & install redmine following instructions here (for this example assume you installed redmine into /var/redmine).  You might have problems with bundler reporting some missing libraries, specifically ‘Rmagick’.  this should help with that.

Install passenger from here

make a symlink from a folder in your apache webroot to the ‘public’ folder of wherever you installed redmine:

ln -s /var/redmine/public /var/www/html/redmine

add an .htaccess file in the ‘public’ folder of redmine containing:

PassengerAppRoot /var/redmine #this should be the physical location of the redmine root directory on the filesystem
RailsBaseURI /redmine #this is the URI you want redmine to be accessible at, should match the symlink in /var/www/html/

Now in your web configuration folder (/etc/httpd/conf.d/) add a new ‘redmine.conf’ (or however you configure apache, it varies across versions of fedora I think) containing this:

Alias /redmine /var/www/html/redmine
<Directory /var/www/html/redmine>
 Options Indexes ExecCGI FollowSymLinks -MultiViews
 AllowOverride All
 Order Deny,Allow
 Deny from None
 Allow from All

Finally we need to set redmine to start in production mode.  Edit /var/redmine/config/environment.rb and uncomment or add the following line at the top:

ENV['RAILS_ENV'] ||= 'production'

And that should be it… restart apache and you should be able to access redmine at:



Download and install the ‘rpm installer’ for Artifactory using the instructions here

before setting up apache we need to change the port the ‘ajp’ connector runs on, as our hudson installation will steal this later.

edit the file: /opt/artifactory/tomcat/conf/server.xml and find the section that looks like this:

<Connector port="8009" protocol="AJP/1.3"
 maxThreads="500" minSpareThreads="20" maxSpareThreads="150"
 enableLookups="false" disableUploadTimeout="true"

change the connector port to “8010” and save the file

In the apache configuration, setup reverse proxying as follows (/etc/httpd/conf.d/artifactory.conf)

ProxyPreserveHost on
<Proxy *>
 Order deny,allow
 Allow from all
ProxyPass /artifactory ajp://localhost:8010/artifactory retry=0
ProxyPassReverse /artifactory/ http://YOURHOST/artifactory/
ProxyRequests Off

Start artifactory (/etc/init.d/artifactory start) and restart apache (/etc/init.d/httpd restart) and that’s it really.  Artifactory is now available at:



Download and install Hudson using the method described here

Create a proxy config file for apache (/etc/httpd/conf.d/hudson.conf) containing the following:

ProxyPreserveHost on
<Proxy *>
 Order deny,allow
 Allow from all
ProxyPass /hudson http://localhost:8080/hudson retry=0
ProxyPassReverse /hudson
ProxyRequests Off

Finally, we need to tell hudson that it’s running on /hudson otherwise all the css / js links break.

edit: /etc/sysconfig/hudson

and edit / change the following line (it’s near the bottom of the file):


Now start hudson (/etc/init.d/hudson start) restart apache and we’re done.  Hudson is now available at http://yourhost/hudson










jNag server rewrite underway

Just a quick not to mention that I’ve restarted work on jNag.  First order of business is to rewrite the server side code which, to be frank, is a quick and dirty job I hacked together just to get soemthing working at the time.  The rewrite will incorporate everything I’ve learned about development in the last couple of years and should be much easier to maintain and offer something that’s caused a few people grief with jNag in the past: version support.  On a basic level this means that jNag will be able to support all the different flavours of nagios that are out there by changing a configuration variable (there’s some detail about how this is done below, in case anyone’s interested)

This new version of the server will be backwards compatible with the current versions of the app, but once I’ve rewritten the server I’ll be moving onto the app.  First I’ll be updating the libraries that jNag uses (especially jQuery mobile, which has had a full production release and a point release since the version that shipped with current versions of jNag) and then hopefully adding features.

As a special bonus, here’s some bits of code that deal with loading in different vetrsions of Nagios.

First up, in the core framework class I have this bit of code:

$this->main = new main();

This ‘includes’ a file based on what’s set in the ‘server_type’ config key, and then instantiates the ‘main’ class from that file.

Our main class looks like this:


 * main class for the 'classic' server type

class main extends main_base{
    function __construct(){
    public function status(){
        $hosts = json_decode($this->run_query("GET columns\nOutputFormat: json\n\n"));
        echo "<table>";
        $last_table = "table";
        foreach($hosts as $host){
            if ($host[2] != $last_table)
            echo "<tr><td COLSPAN='4' ALIGN='middle' style='background-color:red;'>".$host[2]."</td></tr>";
            $last_table = $host[2];
            echo "<tr><td>".$host[0]."</td><td>".$host[1]."</td><td>".$host[2]."</td><td>".$host[3]."</td></tr>";
        echo "</table>";        

Obviously that’s just the first bit of it, the full class is a bit bigger… buut you can see how it implements the ‘Status’ method.  The full class implements all the methods that our old ‘returndata.php’ file in previous versions of jNag provided and, because the framework can be accessed from any index file we can access it through ‘returndata’ and maintain compatibility.

You’ll notice our ‘main’ class extends a ‘main_base’ class.  That looks something like this:

abstract class main_base{
    //show current system status
    abstract protected function status();
    //get items that are 'pinned' to the homescreen
    abstract protected function get_pinned();
    private function format_time($timestamp){
        if ($timestamp > 0){
            return date("d/m/Y-H:i",$timestamp);
        } else {
            return "Never";

And contains abstract functions for all the methods that our real main class has to implement.  So, once all this is in place all you have to do is create a ‘main’ class for your specific flavour of nagios, set the server_type correctly and jNag will work.

Refactoring this code has really shown how much I’ve learned since I wrote the original.  I’m particularly pleased with how the config class turned out.  It looks like this:

class config {
     * load all config information from $filename.
     * for each key = value in the file you end up with config->key = value;
    public function __construct($filename){        
        $settings = file($filename,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        foreach($settings as $setting){
            $var_array = split("=",$setting);
            if (substr($var_array[0],0,2) != "//"){
                $var_array[0] = trim($var_array[0]);
                $var_array[1] = trim($var_array[1]);
                if (strtolower($var_array[1]) == "true")
                $var_array[1] = true;
                if (strtolower($var_array[1]) == "false")
                $var_array[1] = false;
                $variable = $var_array[0];                
                $this->$variable = $var_array[1];
         public function getValue($key){
            return $this->$key;


And, as the comments say, you pass it a file with key / value pairs in the constructor like this:

$config = new config($myfile);

and then you can do something like this:

$foo = $config->getValue("foo");

Anyway, that’s it really… stay tuned for more news as the rewrite progresses.


Possible jNag resurrection

So, jNag was recently listed by ‘Computerworlduk’ as one of the top 16 mobile applications for IT professionals:

And this has given me some motivation to restart development…. I need some sort of project to work on since giving up facebook (I deactivated my account last week in the hope of spurring some productivity) and revisiting jNag might be just what I’m looking for.


Feel free to comment if you’d like to see development restarted, and even shout out some features you’d like to see added if you like.

Note: I doubt I’ll be doing any work on the IOS version of jNag.  I don’t have an apple development platform at the moment, and don’t see me getting one for the foreseeable future (unless I switch jobs and get a new Air as part of my package.  Well, I can dream!) so any future development work will more than likely be android only.

Magento: Email address when using paypal express in 1.6

Just a quick fix for today.  Magento appears to have a bug whereby if a user places an order using paypal express when they’re not logged into your site (ie: using the ‘guest checkout’ functionality of magento their email address will not be passed through correctly.  One annoying symptom of this is that the user won’t receive any order confirmation, or order update emails.


After a lot of debugging I tracked the problem down to the ‘place’ function in /Mage/Paypal/Model/Express/Checkout.php which looks like this:

     * Place the order and recurring payment profiles when customer returned from paypal
     * Until this moment all quote data must be valid
     * @param string $token
     * @param string $shippingMethodCode
    public function place($token, $shippingMethodCode = null)
        if ($shippingMethodCode) {
        $isNewCustomer = false;
        switch ($this->_quote->getCheckoutMethod()) {
            case Mage_Checkout_Model_Type_Onepage::METHOD_GUEST:
            case Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER:
                $isNewCustomer = true;

Here magento uses the ‘checkoutmethod’ to determine which method to call when preparing the ‘quote’ (order to you and me).  The problem is that for whatever reason, orders placed by guests come through with the checkout method set to a blank string.  They then fall through to the default handling method, which doesn’t set the email address.

Some more digging turned up this thread which mentioned the Magento developers were aware of this problem.  A little more googling determined this problem is fixed in 1.7…. so I decided to backport the changes made there into our 1.6 site.

Basically I override the ‘place’ function and invoke a new ‘getcheckoutmethod’ (ripped from the 1.7 class) which sets the correct checkout method.  Then I call the original class method, which trundles off and does whatever it has to.

Here’s the code, I’ll get it packaged up and upload it later on today:

class Tallpaul_Paypalexpressfix_Model_Express_Checkout extends Mage_Paypal_Model_Express_Checkout{

     * Get checkout method blatantly ripped off from magento 1.7
     * @return string
    public function getCheckoutMethod()
        if ($this->getCustomerSession()->isLoggedIn()) {            
            return Mage_Checkout_Model_Type_Onepage::METHOD_CUSTOMER;
        } else {
             return $this->_quote->getCheckoutMethod();

    public function place($token, $shippingMethodCode = null){


Update: here’s the module




Magento: config file mapping

Just had a moment of epiphany that I thought I’d share…I’m sure this is well known to most Magento developers, but I’ve not seen it clearly spelled out anywhere and it’s taken me a while to get my head round this.

Have a look at this snippet of a config file for one of my modules:







Now, notice the lines that say ‘<CustomAttributes>’.  I’ve seen various explanations of what should go in here ranging from ‘it should match the name of your module’ to ‘it doesn’t matter’.  However, I’ve finally figured out what that element is used for.  It’s a mapping string, that maps your classes to an easily used string.

Once I’ve defined my mapping string as ‘CustomAttributes’ magento has a registry that allows me to call my helpers as follows:


This would return the helper located at ‘TallPaul/Customattributes/Helper/helper1.php’.

The same goes for blocks (using something like createBlock) and so on.  It’s probably recommended that you use ‘yournamespace_yourmodule’ as the mapping string, to avoid conflicts, but technically speaking you can use anything you like.

This is confused slightly by the fact that Magento uses a lot of default values in its own code so you’ll rarely see helpers called using mapping strings that make sense.  For example the default helper is called ‘Data.php’.  So when magento makes a call like ‘Mage::helper(‘catalog’); it’s actually asking for ‘Mage_Catalog_Helper_Data.php’.




Magento: solving ‘invalid API path’ errors

I’ve noticed a bug with Magento that affects the code I wrote for the ‘Extending the API’ tutorial so I thought I’d post a fix for it here.  Well, I assume it’s a bug since I don’t see how it could be anything else…

Here’s the article in question

Here’s the api.xml file from that tutorial:

            <modulename_category translate="title" module="modulename">
                <title>New Category API</title>
                	<getID translate="title" module="modulename">
                		<title>Retrieve Category ID from its name</title>

You’ll notice that I declare a ‘resources_function_prefix’ of ‘catalogCategory’ to fit in with the existing Magento API.  this means that my new method called ‘getID’ can be accessed via a soap call to ‘catalogCategoryGetID’.  This all seems straightforward… or so I thought.

It appears that during execution of soap calls, that resource prefix is mapped directly to a magento module.  Hence, if you have a prefix of ‘catalogCategory’ defined in your module any methods with that prefix will be mapped to your module..including the methods defined in the Magento class we’re overriding with our module.  So for all the standard Magento API calls you’ll receive a nice ‘invalid APi path’ error.

The workaround for this is pretty obvious, we need to declare our new methods with a different function prefix and then update the wsi.xml (the wsdl file) accordingly.

This of course means you’ll be accessing your methods with a different prefix than the standard magento one (so something like myModuleCatalogCategoryGetID rather than catalogCategoryGetID) but that’s a small price to pay for it actually, you know, working.


FUSE ESB Development: the build environment

The first thing we need to do when starting to work with fuse ESB is set up our build environment.  For this we’ll be using Eclipse, with the FUSE IDE plugin and maven. I’ll also cover downloading and installing Servicemix, and then generating our first FUSE project.

I’ll be using a windows box for the majority of this tutorial, but it should be fairly straightforward to get this working on your favoured flavour of *nix if you’re so inclined.

There’s a lot to get through here so lets get started.


Download the latest version of maven from here:

Extract the downloaded zip somewhere sensible (I use c:\program files\maven)

To make sure we can use maven from the command line we need to add it to our ‘Path’ environment variable.  On windows 7  you can do this by right clicking ‘Computer’, selecting ‘advanced system properties’ then click the ‘environment variables’ button.  Once the box opens scroll down until you find ‘Path’ under system variables, click edit and add ‘c:\program files\maven\bin’ to your string.

That’s maven setup, lets move onto something a little more challenging.

JDK installation

Before we can install our FUSE products we need to grab the Java Development Kit (JDK) and ensure that Eclipse and servicemix know where to find it by setting our ‘JAVA_HOME’ environment variable.

grab the JDK from here: and run the installer.

Once it’s installed you need to go back into the ‘Environment variables’ box we were in during the Maven installation and add a new variable called ‘JAVA_HOME’ pointing at the Directory your just installed the JDK into (by default it should be something like ‘C:\Program Files\Java\jdk1.6.0_25’).


Download Eclipse Helios from here:

Personally I’ve had no problems using the later ‘Indigo’ version of eclipse, but FUSE recommend Helios for working with their IDE so we’ll stick with that.  Run the installer as usual.  Once it’s installed we need to get the FUSE IDE plugin.

Go here: to download it.  You need to register as part of the ‘Fuse community’, then add the update site listed on that page to your eclipse installation to add the IDE plugin.

Once that’s done the final part of the Eclipse installation is to make Eclipse use our previously installed maven repository rather than the one that comes bundled with Eclipse.  In the Eclipse main menu open ‘Window’->’Preferences’.  Then select ‘Maven’->’Installations’.  Click ‘Add’ and browse to your maven directory (in my case it’s ‘c:\program files\maven’).  Click OK and you’re done.


For our ServiceMix installation I’m going to use the packages provided by FuseSource as ‘Fuse ESB#.  You can get the latest version from here:

Download the latest Zip file and extract it somewhere obvious (c:\servicemix for example).

And…that’s pretty much it, you should now be able to start Servicemix from the command line by changing to the servicemix directory and running the command ‘bin\servicemix.bat‘.  If everything goes well you should see something like this:

Generating our first project

Now everything’s setup lets try generating and deploying a simple test project.

To generate our project structure we can use the maven archetype generator as follows:

First create a new, empty directory where we’ll be working (I’m using c:\testproject)

Next run the command ‘mvn archetype:generate‘. this will prompt maven to download the latest list of archetypes and display them as a huge list.  Luckily we can filter this list if we know the name of the archetype we want. Type ‘karaf-blueprint-archetype‘ to return our filtered list, which should have 1 entry.  press 1 to select this entry and then from the list of versions that follows select the latest one (at time of writing this is 2.2.7).

You’ll now be prompted for some information about the project, use the following (or replace with your own values as required):

Once you’ve entered the details a simple ‘Y’ will confirm them and generate our project.

Import project into eclipse

The next stage is to import our project into Eclipse.  In the Eclipse main menu select ‘File’->’Import’, then from the import box scroll down and select ‘Maven’->’Existing Maven Projects’.

In the Import window browse to the location of your test project (notice maven has generated it in a subdirectory of our root directory) as shown:

Click ‘Finish’ to import the project.  You may get a ‘NullPointerException’ here for some reason, but the project should still import.

Configure a ‘Hello World’ Route

Now let’s make our project actually do something.

If you open up your project structure, you should see something like this:

There’s a few files and folders here to take note of, and we’ll go into these in more detail in further installments of this series, but for now let’s just jump in and start hacking around.

First of all we can remove the sample java beans that Maven gives us, so delete the two files under your java class path.  In my case this is ‘test-project\src\main\java\uk\co\tall-paul’.

We can also get rid of the sample camel context (in the file ‘my-service.xml’) and replace it with our own.  So remove that file and create a new one in the same place called ‘hello-world.xml’ containing the following:

<blueprint xmlns="" 
	<camelContext id="hello-world-context" xmlns="">
	<route id="hello-world-route">
		<from uri="timer:test?period=5000"/>
		<log message="hello world!"/>
		<to uri="mock:result"/>

For more info on what that xml actually does have a look at the camel website, and specifically the information abou the various components which you can find here:

This simple route basically uses a timer to fire off a message every 5 seconds, then logs ‘hello world’ to the console.

Build the Project

To build the project we can run maven from the command line or from within eclipse.

From the command line, navigate to the project folder (c:\testproject\test-project) and run the command ‘mvn clean install‘ (you can find more information about various maven commands here: , but really ‘mvn clean install’ is all you’ll need for now).  If all goes well you should (eventually) see something like the following:

From Eclipse you can do pretty much the same thing by right clicking on your project and selecting ‘Run as’->’Maven install’ as shown here:

Our project has now been compiled and installed into our maven repository, which you can see if you browse to the location of the repository (by default this is at c:\users\YOURUSERNAME\.m2\repository):

Deploy the project into servicemix

Thanks to the integration of maven into servicemix, deploying our project is trivial using the ‘install’ command withing servicemix.

From your servicemix consolerun the following command:

install mvn:\\test-project\1.0-SNAPSHOT

Hopefully you should be given a bundle ID, something like ‘bundle ID: 200’.  Now type the following command (substituting the bundle ID you were given for the 200 shown here)

start 200

And that should be it.  As long as you didn’t see any error messages, your bundle should now be running.  To see the output from our route type the following commands (the first clears the log file for clarity, the second displays the current log)


after a few seconds you should see something like this:

You can see that every 5 seconds our timer is going off and logging our message to the console. You can also see here the name of the component the message originated from (remember we put ‘test’ as the first parameter for our timer component) and the route in which the message is running (here it’s ‘hello-world=route’), which indicates the importance of naming your routes and components sensibly.


Hopefully this brief tutorial has shown how easy it is to get up and running with Fuse ESB development.  Next time we’ll look at how to use the environment we’ve set up here to do some real integration work.

FUSE ESB Development Introduction

Today I’ve decided to move on from the magento stuff I’ve been posting recently, and start looking at working with FUSE ESB.  This is an enterprise service bus based on apaches servicemix platform.

There’s a lot of new stuff here, the majority of the code for servicemix is written in Java and plugged together using the Blueprint XML language and apache camel.  For the build environment we’ll be using apache maven, eclipse and subversion (for source control).  Over the coming days (weeks) I’ll describe how to set all this up from scratch and develop a web services API that will allow you to access data from a lot of different systems using one interface.  I’d never used Java or Maven before getting started on this project, so that should be a good indicatoof how easy this stuff is to pick up.

For more info on what fuse ESB is, and what it does you should go and read my post from a few weeks ago: Overview of FUSE ESB.  As a quick summary the idea of an ESB is to allow various systems to interoperate in a standard, maintainable way.  There’s a lot more to it than that of course, but that’s the basic idea.

As a starting point for this series of tutorials, lets have a look at what we’ll be covering (subject to change, I’ll update this list if I change things around)

1: Setting up the build environment

1a: downloads

1b: maven setup

1c: subversion setup

1d: eclipse setup

1e: servicemix / FUSE ESB setup

1f: creating our project structure

2: Defining our APIs

2a: creating a wsdl

2b: generating our API code

3c: generating code from a 3rd party wsdl (magento)

3: Implementing our API

3a: creating our camel route

3b: plugging it all together

4: Useful resources

Okay, that’s all for today, check back over the weekend when I’ll hopfully have written up the ‘setting up the build environment’ section.

jNag for IOs binary

  • April 21, 2012
  • jNag

As promised last week I’ve uploaded the IOS binary for jNag, so you can deploy it to a local device.  I’ve no idea how you go about doing this, and no-one’s gifted me a free Macbook yet so I can’t test this in any way.  Hopefully this helps out if you want to get jNag on your iDevice now it’s been removed from the App store.


Magento: Block users using a group

One of the most glaring omissions of magento is a way of blocking users. Currently your only option when faced with a user you wish to ban from the site is to do some kind of IP blocking in your web server configuration, or delete the user account. Neither of those options is really useful, IP blocks are difficult to maintain and if you delete a user account there’s nothing to stop the miscreant just creating a new account.

Here’s how you can create a simple module that allows you to set up a ‘blocked’ group. When customers are placed in this group they can still log in, however they’re not able to add anything to their cart and thus can’t actually place an order.

First up we need to create our module folder structure. Here I’m using a namespace of ‘Tallpaul’ and a module name of ‘bangroup’, but you can change these according to your requirements (just make sure you change them throughout, or you’ll get missing classpaths and all sorts of fun).







You can see we only have a couple of files in here.  ‘Config.xml’ is a configuration file that defines how our module is going to behave, and’CartController.php’ is our code that’s going to stop blocked users from adding items to their cart.

Lets take a look at ‘config.xml’ first.

 <tallpaul_bangroup before="Mage_Checkout_CartController">Tallpaul_Bangroup_Frontend_Checkout</tallpaul_bangroup>

After the usual configuration stuff of defining a version for our module, there’s only one line here that does anything.  We’re defining that our module (tallpaul_bangroup) is overriding a specific controller (Mage_Checkout_CartController) with a new controller (Tallpaul_Bangroup_Frontend_Checkout).  And that’s pretty much it.  You can see here that controller overrides are dfined in a different way to bloc / helper / etc overrides.  I’m not entirely sure why, but this is just the way magento does it.  It usually takes me a bit of trial and error to work out how to override a specific controller, but once you’ve done it a few times it becomes second nature to map these files to a classpath.

Anyway, now we’ve got our configuration out of the way, lets have a look at CartController.php.  In here we’re going to extend the magento checkout controller class, and override a couple of functions.

 * Shopping cart controller
require_once Mage::getModuleDir('controllers', 'Mage_Checkout').DS.'CartController.php';

class Tallpaul_Bangroup_Frontend_Checkout_CartController extends Mage_Checkout_CartController

   * Check if user is blocked (ie: if they're in the 'Blocked' group)
  public function isBlocked(){
  			$groupId = Mage::getSingleton('customer/session')->getCustomerGroupId();
			$group = Mage::getModel('customer/group')->load($groupId);
			$groupName = $group->getCode();
			if ($groupName == "Blocked"){
				return true;
		return false; //user can't be blocked if they're not logged in

   * Empty current user cart
  public function emptyCart(){
  		$cartHelper = Mage::helper('checkout/cart');
     	$items = $cartHelper->getCart()->getItems();
        foreach ($items as $item) {
            	$itemId = $item->getItemId();

     * Shopping cart display action
   public function indexAction()
   		if ($this->isBlocked()){

     * Add product to shopping cart action
    public function addAction()
			if ($this->isBlocked()){




The actions we’re overriding are ‘indexAction’ (which is used to display a user’s cart) and ‘addAction’ (called whenever an item is added to the cart.  The other 2 functions here ‘isBlocked’ and ’emptyCart’ are simple helper functions I’ve defined to keep things tidy.

The basic logic here is that when a user adds an item to their cart we first check if they’re in a specific group (here I’ve used ‘Blocked’ as the name of the group, but you can call it something else as long as you update the code in ‘isBlocked’).  If they are we empty their cart, and then return.  The effect of this is to take the user to an empty cart with nothing added to it and no error message to indicate what the problem might be.

As a doublecheck against a user who already has items in their cart when they’re placed in the ‘Blocked’ group, we also add a check to the ‘indexAction’ to empty their cart when they view it.

At the end of our indexAction and addAction we use the nifty trick of calling the relevant method in the ‘parent’ class.  This means our code remains clean and simple, while allowing requests that we don’t want to block using our code to ‘fall through’ to the parent implementation and be dealt with by the standard Magento logic.

Now all that’s required is an ‘activation’ xml file in app/code/etc and we’re good to go.  Create a group called ‘Blocked’, add your rogue users to it and laugh as you imagine their confusion at being unable to add items to their basket.

You can download the code for this article here: