Paypal IPN integration for CakePHP 1.3

Note: This guide applies to CakePHP 1.3 but may be adapted with relative ease to work with CakePHP 2.x. The guide is based on code from an old project and is intended for use as a guide, it is not a comprehensive “How To”. For the purposes of this guide, I have removed a lot of the more complex back office functions which don’t really serve a purpose here.

I am not going to go through showing you how to set up user auth, access control and shopping cart functionality. It has been documented to death, in this guide I am focusing solely on Paypal integration.

So, you have a CakePHP based web application with a functional shopping cart and you want to integrate it with Paypal IPN? Well you are in the right place! Before we get started, you’ll need to understand a little bit about the Paypal IPN flow.

Paypal IPN flow

  1. User on your site submits a Paypal Button (a standard form in this case) to the Paypal servers.
  2. Paypal sends an Instant Payment Notification (IPN) http request to a predefined handler/listener on your site.
  3. Assuming you have defined the correct IPN URL, your IPN handler will return a HTTP 200 OK response code (otherwise Paypal will keep sending IPN notifications for up to four days).
  4. Your IPN handler needs to send the complete unmodified notification (as received from PayPal) back to Paypal to validate the notify request. You can do this by prepending the notification with “cmd=_notify-validate”.
  5. Upon receiving this request, Paypal will reply with a single word – either “VERIFIED” or “INVALID”.
IPN Overview diagram from developer.paypal.com

IPN Overview diagram from developer.paypal.com

As you can see, it is pretty straight forward process. With the flow above in mind, we will need to do a number of things to get our CakePHP based checkout up and running.

  1. Setup a Paypal Website Payments Standard compatible form that submits to paypal.
  2. Create a listener/handler to handle the Paypal IPN and perform some back office tasks on your site.

The Paypal Form

So, first things first, lets create the Paypal form that contains all of the items in our shopping cart. All of the more important form fields are described in the code snippet below.

<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
    <input type="hidden" name="business" value="<?php echo Configure::read('SiteSettings.paypal_business'); ?>"> <!-- pass in the email address of the Paypal merchant account holder -->
    <input type="hidden" name="cmd" value="_cart"> <!-- here we specify the type of Paypal button we are using - in this case it is a shopping cart button (as opposed to buy now, subscribe or donate button - among others available) -->
    <input type="hidden" name="upload" value="1"> <!-- we are going to create a cart containing individual items. We will pass individual items to paypal via the Cart "upload" option -->
    <input type="hidden" name="currency_code" value="EUR"> <!-- specify the transaction currency -->
	
    <!-- the "custom" field below allows us to pass in any custom data from our form right through to our IPN handler. -->
    <input type="hidden" name="custom" value="<?php echo $user_id; ?>">
    <input type="hidden" name="return" value="http://mydomain.ie/thankyou"><!-- specify the url of you want to the user to be forwarded to on return from Paypal after successful payment -->
    <input type="hidden" name="cancel_return" value="http://mydomain.ie/cancel"><!-- specify the url of you want to the user to be forwarded to on return from Paypal after aborted payment -->
    <input type="hidden" name="notify_url" value="http://mydomain.ie/ipn" /><!-- specify the url of your IPN handler -->

	<!-- Customer details -->
    <input type="hidden" name="first_name" value="<?php echo $customerFirstName; ?>">
	<input type="hidden" name="last_name" value="<?php echo $customerLastName; ?>">
    <input type="hidden" name="address1" value="<?php echo $customerAddress1; ?>">
    <input type="hidden" name="address2 " value="<?php echo $customerAddress2; ?>">
    <input type="hidden" name="street" value="<?php echo $customerStreet; ?>">
    <input type="hidden" name="city" value="<?php echo $customerCity; ?>">
    <input type="hidden" name="state" value="<?php echo $customerState; ?>">
    <input type="hidden" name="zip" value="<?php echo $customerPostCode; ?>">
    <input type="hidden" name="country" value="<?php echo $customerCountry; ?>" />
    <input type="hidden" name="email" value="<?php echo $customerEmailAddress; ?>" />
    <input type="hidden" name="contact_phone" value="<?php echo $customerPhone; ?>" />
   
      
	<?php
		/*
		* You can submit multiple items from your shopping cart. Each item can have a group of fields 
		* that can describe the product including the name, price (unit), quantity and an item number 
		* (i.e. a stock code or cart item reference number)
		* Each of these items is diffrentiated by appending a sequential number (for each item) to 
		* each field name in the group.
		* For example:
		* Fields for the first item include: item_name_1, amount_1, quantity_1, item_number_1
		* Fields for the second item include: item_name_2, amount_2, quantity_2, item_number_2
		* Fields for the third item include: item_name_3, amount_3, quantity_3, item_number_3
		* ... and so on
		*
		* So now we will loop through each item in the cart and create a group of fields for each item.
		*
		*/
		
		$itemNumber = 1; 
	
		foreach ($cartitems as $cartItem):
	 
			$price = $cartItem['Product']['price'] * $cartItem['CartItem']['quantity']; //calculate the cart total
			$cartTotal += $price;
	?>

            <input type='hidden' name='item_name_<?php echo $itemNumber; ?>' value='<?php echo $cartItem['Product']['title']; ?>'>
            <input type='hidden' name='amount_<?php echo $itemNumber; ?>' value='<?php echo number_format($cartItem['Product']['price'],2); ?>'>
            <input type='hidden' name='quantity_<?php echo $itemNumber; ?>' value='<?php echo $cartItem['CartItem']['quantity']; ?>'>
            <input type='hidden' name='item_number_<?php echo $itemNumber; ?>' value='<?php echo $cartItem['CartItem']['id']; ?>'> 

	<?php 
		endforeach; 
	?>
            
    <input type='hidden' name='handling_cart' value='<?php echo number_format($shippingPrice, 2) ?>' /> <!-- add your shipping/handling fee -->
    <input type="submit" name="submit" value="Proceed to Payment" />
</form>

The form is pretty ordinary as far as HTML forms go. You need to make sure that all of your URLs are correct and that you are populating the Cart items correctly in the form.

 

The IPN handler

Now that we have our form submitting all of our cart details to Paypal, we need to setup our IPN handler that will validate the IPN notification, save the order details and associated items and remove items from the customers cart.

Our controller looks like this.

<?php
class InstantPaymentNotificationsController extends AppController {
    /**
     * InstantPaymentNotificationsController
     * 
     * A custom controller to handle Paypal Instant Payment Notifications. 

     */
    var $name = 'InstantPaymentNotifications';
    
	var $uses = array('InstantPaymentNotification', 'CartItem', 'Product', 'User', 'Order', 'OrderItems');

	
	/*
	* process_ipn() is our IPN request handler. Traffic is routed here via /ipn as defined app/config/routes.php
	*/
	
    public function process_ipn(){

		// Paypal (we are assuming) has sent an IPN message to our IPN handler. Ensure that the POST is not empty.
        if(!empty($_POST)){
			
            //NOTE: at this point you could add optional validation here to verify that
            // 1. the cart contents match what was submitted to Paypal
            // 2. the cart value matches the Paypal transaction total
            
			//Now we need to validate the notification sent by Paypal. You do this by returning the notification 
            //back to paypal with an additional variable (cmd=_notify-validate) in front of the POST string.
            //add 'cmd' 'notify-validate' to a transaction variable
            $postback = 'cmd=_notify-validate';
            //and add each individual parameter that Paypal sent to the transaction
            foreach ($_POST as $key => $value) {
                $value = urlencode(stripslashes($value));
                $postback .= "&$key=$value";
            }
            //create headers for the post back
            $header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
            $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
            $header .= "Content-Length: " . strlen($postback) . "\r\n\r\n";
			
			//Check to see if we are working with the Paypal sandbox or the production server - a handy switch for developers to ensure you are working with the correct gateway
            //If this is a sandbox transaction then 'test_ipn' will be set to '1'
           	if(isset($_POST['test_ipn'])){
                $server = 'www.sandbox.paypal.com';
            }else{
                $server = 'www.paypal.com';
            }

            //create a socket connection to the relevant Paypal over SSL
            $socket = fsockopen ('ssl://'.$server, 443, $errno, $errstr, 30);

            //If we can't open a socket - log an error.
            if (!$socket) {
                $this->log('HTTP Error in InstantPaymentNotifications::process while posting back to PayPal: Transaction='.$transaction);
            }else{ 
				// we opened the socket successfully. Write the request to the open socket.
                fwrite($socket, $header.$postback);
				
                while (!feof($socket)) {
                    $response = fgets($socket, 1024);//get the response from paypal
					
					
                    if (strcmp ($response, "VERIFIED") == 0) { //paypal transaction verified.

						//save the IPN details to the IPN table - optional, but I like to maintain a record of all of my paypal transactions.
                        $notification = array();
                        $notification['InstantPaymentNotification']=$_POST;
                        $this->InstantPaymentNotification->save($notification);
                        
						//go ahead and process the order (i.e. perform your back office logic.
						$this->processOrder($_POST);
						
						
                    }else if (strcmp($response, "INVALID") == 0) {
                        //The paypal transaction has not been verified, so the order is invalid. Log the details.
                        $this->log('Found Invalid:'.$transaction);
                    }
                }
                fclose ($socket); //close your socket to the paypal server.
            }
        }

    }
	
    private function processOrder($ipnFields){
		
		$orderError = false;
		
        //get the user id from the "custom" passthrough variable (as added to the Paypal form.
		$user_id = $ipnFields['custom'];
		
		//setup your order data
		$this->data['Order']['user_id'] = $user_id;
		$this->data['Order']['address1'] = $ipnFields['address1'];
		$this->data['Order']['address2'] = $ipnFields['address2'];
    	$this->data['Order']['street'] = $ipnFields['street'];
        $this->data['Order']['city'] = $ipnFields['city'];
        $this->data['Order']['state'] = $ipnFields['state'];
        $this->data['Order']['zip'] = $ipnFields['zip'];
        $this->data['Order']['country'] = $ipnFields['country'];
        $this->data['Order']['email'] = $ipnFields['email'];
        $this->data['Order']['contact_phone'] = $ipnFields['contact_phone'];
		$this->data['Order']['txn_id'] = $ipnFields['txn_id'];
	    $this->data['Order']['status'] = $ipnFields['payment_status';
        
		//if the payment status is pending create an order from the values passed in
		if($ipnFields['payment_status'] == 'Pending') // Pending status is ok.
		{
			$this->Order->create();
			$this->Order->save($this->data['Order']);
		}
		else //if the order is complete or failed, update the order (as previously inserted).
		{
			//perform a lookup based on the txn_id/txnid
			$id = $this->Order->find('first', array('fields' => array('Order.id'),'conditions' => array('Order.txnid' => $fields['txn_id']))); 
			
			//perform an update
			$this->Order->id =$id; //load the existing order record
			$this->Order->save($this->data['Order']); //save the updated order details in the same record to avoid duplicates in the DB
            
            if($fields['payment_status'] != 'Completed') //anything other than completed should be investigated.
    		{
                $orderError = true;
			}
        }
		//get the id of the order
		$order_id = $this->Order->find('first', array('fields' => array('Order.id'),'conditions' => array('Order.txnid' => $fields['txn_id']))); 
		
		
		//Loop through all items passed in from the Paypal notification.
		$itemNumber = 1; // set the initial item number
        while(isset($fields['item_name_'.$itemNumber])){
			
			//get the product id of the current item from the cart based on the unique cart id passed in the 'item_number_X' field.
			$product_id = $this->CartItem->find('first', array('fields' => array('CartItem.product_id'),'conditions' => array('CartItem.id' => $fields['item_number_'.$itemNumber],'CartItem.user_id' => $user_id))); 
			
			//add the product details to the OrderItems table.
			$this->data['OrderItem']['product_id'] = $product_id;
			$this->data['OrderItem']['quantity'] = $fields['quantity_'.$itemNumber];
			$this->data['OrderItem']['order_id'] = $order_id;
			
			$this->OrderItem->create();
			if ($this->OrderItem->save($this->data['OrderItem'])) {
				//remove the corresponding item from the users shopping cart
				$this->CartItem->delete($fields['item_number_'.$itemNumber]);
			}
			else
			{
				$this->log('Couldn\'t save order item. Product ID: '.$fields['item_number_'.$itemNumber].' Order ID: '.$order_id);
				$orderError = true;
			}
            $itemNumber += 1;
        }
		

		
		
		if($orderError) //if there hasn't been an error with the order - send out mail to the site admin and the user
		{
			//send the site owner details about the problem order so that they can follow up with the customer.			
		}
		else
		{
			//perform other logic here such as send confirmation emails		
            //also be sure to add a check to see if the payment status is Pending or Complete.
		}
    }
	
	
}
?>



The critical part here is the “process_ipn” function. This is the function that validates the IPN notification. The “processOrder” function is not complete and is purely for demonstrative purposes and to give you an idea of how you might parse the the IPN details and start your back office workflow.

Routes to the handler

You may have noticed in the form code above, that I am pointing Paypal to some nice clean URLs, but can’t figure out how these line up with my IPN controller. I use CakePHP routes for nicer URLs.

See app/config/routes.php

<?php
    /* Paypal IPN Routes */
    Router::connect('/ipn', array('controller' => 'instant_payment_notifications', 'action' => 'process_ipn'));
    Router::connect('/thanks', array('controller' => 'orders', 'action' => 'thanks'));
    Router::connect('/cancel', array('controller' => 'pages', 'action' => 'cancel'));
?>

Validation Methods

This guide uses postbacks to validate your transaction. The preferred way to validate your transaction is via “shared secret” because it ensures the validity of the data while decreasing traffic two/from your site. However to implement this approach, you need dedicated hosting (not shared), SSL and you need to be using Encrypted Website Payments – making the postback method much cheaper to implement.

 Further Security for the postback method

Worried about the potential for fraud in the postback method? Don’t be! The great thing about the postback method is that you can compare the items in the IPN against customers cart contents (and transaction value) before returning the IPN back to Paypal (see comments in the controller code above) to check that the initial post variables have not been modified.

You can also perform the same (or a similar) check after you have received the “Verified”/”Invalid” message if you need to automate some back office tasks such as locking a user account or mailing an in-house fraud team.

 

Sending E-Mail from CakePHP 2.x

A few years back I was working on a customer project in CakePHP 1.3. I must say I fell in love with CakePHP, it made life so easy, but the one aspect that I always had difficulty with was sending E-Mail. Well, sending E-Mail was no problem, applying HTML templates was a bit cumbersome.

I am currently working on a project which is based on CakePHP 2.3 and recently I have been working on features that need to be able to send E-Mail notifications. I was dreading looking into this, but as of CakePHP 2 the process has gotten a whole lot easier. There is still a little bit of configuration to be done, but it all works beautifully.

The CakePHP 2.x book is a little confusing to read, so here are the steps I took to get E-Mail up and running in my application.

Setup

First things first, you need to open app/Config/email.php.default and save it as email.php. In your new email config file, add a basic transport details (multiple transports can be created and used). In the example below, I have created a basic SMTP transport which sends email in both plain text and HTML by default.

//app/Config/email.php
class EmailConfig {
    
	public $smtp = array(
		'transport' => 'Smtp', //transport method
		'from' => array('noreply@denishogan.ie' => 'denishogan.ie'), //an array containing the "from" address and sender name
		'host' => 'mail.server.net',//add your SMTP mail server here
		'port' => 25, //port your outgoing mail should be sent via. 25 is normally fine for SMTP, but may be different for some mail services
		'username' => 'usernameForMyAccount',//the username for my account
		'password' => 'passwordForMyAccount',//the password for my account
        'emailFormat' => 'both'//you can select text, html (or both)
		'log' => false, //log the email headers and message
	);
    
}


In your Controller

Make sure that you are loading the CakeEmail class at the top of your controller. Then to send your E-Mail, simply specify recipients, a subject and the name of the view you wish to use to display your email.

In this case, I have added a few more details – I have chosen to override the default sender details as specified in the SMTP transport config. I am also passing in a few variables for use in the E-Mail views.

//in your controller.php
App::uses('CakeEmail', 'Network/Email'); // this should be placed before your Controller Class

class UsersController extends AppController {
    public function add() {
        if($this->request->is('post'))
        {
            //do something clever with the request data
            if($this->User->save($this->request->data))
            {
                $newUserID = $this->User->id; //get the id of the newly saved/inserted user.
                
                //send an email to say that the user data was saved
                //Start email functionality
                $recipients = array('recipient1@here.ie','recipient2@there.com');//multiple recipients can be defined in an array.
    			$from = array('denis@blah.ie' => 'Denis Hogan'); // Say who the email is from if you wish to override the settings in your transport config.
				$subject = "New User added to this wonderful CakePHP application: ".$this->request->data['User']['name']; //Set your subject line.
				$viewVars = array('user_id'=>$newUserID,'user_name'=> $this->request->data['User']['name']);//pass an array of variables to the email view if required - no need for this in static messages.
				$template = 'new_user_template';// specify the name of the email template you wish to use.
				$this->sendMail($recipients, $from, $subject, $viewVars, $template);
				//End email functionality
                
				$this->Session->setFlash(__('The User has been saved'), 'default', array('class' => 'message success'));// set a flash message to confirm that the user details were saved.
				$this->redirect(array('action' => 'index')); //redirect back to the index view.
            }
            else
            {
                //the user details could not be saved :( - display an error
                $this->Session->setFlash(__('The user details could not be saved. Please, try again.'));
            }
        }
        else
        {
            //The user has not submitted data, so display the add view - irrelevant in this example.
        }
    }
}


Plain Text & HTML Layouts

Ok, we are making progress. But there are a few more jobs to be done before we can send our mail. The first thing you need to do is to create both plain text and HTML layouts. You can find the defaults for both in app/View/Layouts/Email/ in their corresponding folders.

If you plan on using an array of different designs for sending email from your application (i.e. for sending out seasonal promotions, internal/external branding, etc.), you can create multiple templates here, each with bespoke CSS for the mail you wish to send.

For the purpose of this example, I am going to keep it simple and use the default layout (default.ctp).

Plain Text & HTML E-Mail Views

E-Mail view are different from layouts in that layouts allow you to define the look and feel of your (HTML) E-Mail (i.e. they contain the structural HTML and CSS). However Views allow you to format the content as you see fit.

As you saw in the snippet above, I chose to use an email view called “new_user_template” and passed in two variables, a user ID and a user name. I will need to create a view with a message in it to display this information. But ,because I am sending the email in both Plain Text and HTML formats, I will need to create a template for each format.

To do this, make your way to app/View/Emails/html and create a new file called new_user_template.ctp. In this file, you can format your E-Mail body content as you would like using HTML. You can access your variables as passed in the $viewVars variable in the controller (see above).

Here is an example of a HTML view that uses the variables passed in from the controller above.

<p>A new user has been created. The user's name is <b><?php echo $user_name; ?></b>.</p>
<p>You can view their details here: <a href="http://myxyzapp.com/users/view/<?php echo $user_id; ?>">http://myxyzapp.com/users/view/<?php echo $user_id; ?></a></p>


The plain text alternative is very similar – however it doesn’t contain any HTML.

A new user has been created. The user's name is <?php echo $user_name."\n"; ?>.
You can view their details by copying the following link into your browser: http://myxyzapp.com/users/view/<?php echo $user_id."\n"; ?>


And that’s all there is to it! I have only scratched the surface here, there is much more you can do on both the configuration and templating of emails.

 

 

Simple Ajax request with jQuery

Thanks to the jQuery library, Ajax transactions are a breeze.  All you have to do is send an Ajax request off to the server and display the results. The example below sends a request to random_number.php on the server when the user clicks on a button. When a result is successfully returned, it is displayed in a div and the style of the div is updated.

In your view file, create your button and result div. Then add a JavaScript listener to your button which will send the Ajax request when the button is clicked.

    <!--This is the button -->
    <button type="button" class="btn btn-lg btn-primary" id="generateButton">Generate Number</button>
    
    <!-- This is the div that contains the result -->
    <div class="panel-default" id="result_div">
    	The result will appear here.
    </div>
    
    <!-- This is the JavaScript that listens for a click and then sends the request -->
    <script>
    	$("#generateButton").click(function(){ //when "generateButton" is clicked
			$.ajax({ //create an ajax request
				url:"http://showcase.denishogan.ie/ajax/random_number.php", // this is the url to send the request to
				success:function(result){ // on success
					$("#result_div").html(result); // display the resulting html (or in our case, simple old text) in the div with an id of "result_div"
					$("#target-panel").removeClass("panel-default"); //as we are at it, remove the old style of the result container
					$("#target-panel").addClass("panel-success"); //add a new style so it turns green
				}
			});
		}); 
	</script>
    
    

On the server side, we have a tiny file called random_number.php which simply outputs a random number between 0 and 9999.

<?php
    //output a random number between 0 & 9999
    echo "<b>Random Number:</b> ".rand(0, 9999);
?>

This example is extremely simple, but it doesn’t get a whole lot more complicated as jQuery also makes it very easy to handle:

  • Errors.
  • Different datatypes – XML, Json, script and HTML.
  • Caching
  • Handle specific HTTP responses (i.e. 404)
  • and perform a variety of other functions;

See the jQuery docs for more information about jQuery.ajax().

You can run this script here. Just click the button on the right hand side to generate a new random number (over and over again if you wish!).

Create a basic XML document with SimpleXML

Following on from yesterdays post, you can also use SimpleXML to quickly create XML documents.

The example below creates a very short and basic XML document containing a list of cars that I would love to have in my dream garage (well, some of them anyway).

$vehicles = array(
		"car1" => array(
			"make" => "BMW",
			"model" => "M3",
			"capacity" => "2500 c.c.",
			"fuel" => "Petrol"
		),
		"car2" => array(
			"make" => "Alfa Romeo",
			"model" => "155 V6 TI DTM",
			"capacity" => "2500 c.c.",
			"fuel" => "Petrol"
		),
		"car3" => array(
			"make" => "Ferrari",
			"model" => "F40",
			"capacity" => "2936 c.c.",
			"fuel" => "Petrol"
		)
	);
	
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><dreamgarage></dreamgarage>');
$xml->addAttribute('version', '1.0'); //add a version attribute
$xml->addChild('generated', date('Y-m-d H:i:s')." ".date_default_timezone_get());
	
$xml->addChild('owner', 'Denis');
$cars = $xml->addChild('cars');

foreach($vehicles as $vehicle) // loop through all vehicles in the garage array
{
	$car = cars->addChild('car'); // add a child car node for that vehicle
	
	foreach($vehicle as $key => $value) // loop through all attributes of each vehichle and add a child nodes for each vehicle attribute
	{		
		if($key == "fuel") //treat fuel type differently to all other key value pairs 
		{
			$children = $car->children();
			$children->capacity->addAttribute($key,$value); //add the fuel type as an attribute of the engine capacity node
		}
		else
		{
			$car->addChild($key, $value); //add the key value pair as a child node of car.
		}
	}
}

$location = $xml->addchild('location');
$location->addchild('country', 'Ireland');
$location->addChild('town', 'Limerick');
	
$xml->asXML('demo.xml'); //save xml to file (use asXML() with no arguments to display clean XML rather than output to file.)
    
    

The resulting XML looks like this.

<dreamgarage version="1.0">
    <generated>2013-10-21 20:04:51 UTC</generated>
    <owner>Denis</owner>
    <cars>
        <car>
            <make>BMW</make>
            <model>M3</model>
            <capacity fuel="Petrol">2500 c.c.</capacity>
        </car>
        <car>
            <make>Alfa Romeo</make>
            <model>155 V6 TI DTM</model>
            <capacity fuel="Petrol">2500 c.c.</capacity>
        </car>
        <car>
            <make>Ferrari</make>
            <model>F40</model>
            <capacity fuel="Petrol">2936 c.c.</capacity>
        </car>
    </cars>
    <location>
        <country>Ireland</country>
        <town>Limerick</town>  
    </location>   
</dreamgarage>


You can run this script here and you can view the output XML file here. The time in the “generated” node will be updated every time the script is run.

Parsing XML with PHP5 and SimpleXML

Since the introduction of PHP5, a range of XML handling functions have been added to PHP core. Why is this important I hear you ask? Well it makes life easier if you work with XML!

Below is a small script that uses SimpleXML loads and parses an RSS feed (which is XML) from this site. The feed is generated automatically by WordPress. It then displays some of the feed meta data and a list of posts contained in the feed.

<?php
    //use simplexml to load the feed
	$feed = simplexml_load_file('http://www.denishogan.ie/feed');
	
	//display the feed meta data
	echo 'Title: '.$feed->channel->title.' - '.htmlspecialchars($feed->channel->description).'<br/>'; //htmlspecialchars is used here because I happen to have <ul> used in the description of my feed.
	echo 'Site Link: <a href="'.$feed->channel->link.'" target="_new">'.$feed->channel->link.'</a><br/>';
    //search for all children of channel with a prefix of atom (of which there is one in this feed).
	$atom = $feed->channel->children("atom", true)->link->attributes(); 
	echo 'Feed Link: <a href="'.$atom["href"].'" target="_new">'.$atom["href"].'</a><br/>';
	
    
    foreach ($feed->channel->item as $entry):
		echo 'Post title: <a href="'.$entry->link.'" target="_new">'.$entry->title.'</a><br/>';
	
		$time = strtotime($entry->pubDate);
		$newformat = date('d-m-Y H:i',$time);
		echo 'Published: '.$newformat;

    endforeach;
?>

Full SimpleXML documentation can be found here.

And you can see the code in action here.

How to deal with Ajax Caching in IE

Warning, this is a techie post.

I recently had an issue with Internet Explorer recently when using getJSON  to get a result set from the server, however the client browser was displaying an old version of the result set. After a little digging, I found that IE caches ajax transactions by default. Personally, I think this is daft, as I generally want to request new data from the server the majority of the time. From this point of view, I think it would be better to have caching disabled by default – but there you go!

To get around this problem, simlply disable ajax caching after page load (i.e. $(document).ready()).

$(document).ready(
    function() {
        $.ajaxSetup({ cache: false });
    });
    
    /*
    //After that, you can call $.getJSON as normal!
    $.getJSON(
        url,
        function(data) {
            //do important things with your data here,
            //safe in the knowledge that IE is no longer caching old responses! \o/
        });
    */
});

Cutting Barley

 

20130830-154151.jpg

Focal Length
4.28mm
Aperture
f/2.4
Exposure
1/1004s
ISO
50
Focal Length
4.28mm
Aperture
f/2.4
Exposure
1/1004s
ISO
50
Focal Length
4.28mm
Aperture
f/2.4
Exposure
1/1004s
ISO
50

 

Took this while out taking a few photos for a friend’s website. Taken on an iPhone 4s.

After years of building custom themes for my site, I have just switched the site theme to Twenty Thirteen – a new theme that ships with WordPress 3.6. I am loving it so far. I think I might just keep it! :)

Although I do have to go back through my old posts to select the post format for each one as the bulk post update feature doesn’t support formats.  :(

Sort listboxes that have been populated with JSON results.

I was working on a CakePhp project recently and had an Ajax response returning data in the incorrect order even though CakePhp was actually sorting the data correctly.  I thought I would post this handy snippet for anyone that is in a similar position.

//Sort listbox after population with JSON result.
function Sort(elementId) {
    // Convert the listbox options to a javascript array and sort (ascending)
    var sortedList = $.makeArray(
        $("#" + elementId + " option")).sort(function(a, b) { return $(a).text() < $(b).text() ? -1 : 1; }
    );

    // Clear the options and add the sorted ones
    $("#" + elementId).empty().html(sortedList);
}

Enjoy!

Forge

IMG_3586

I was down at the local Iron Works a few days ago to get a few photos for their new website. It was fantastic to see such skill and craft in my home town. I think I appreciated it even more because it is so far removed from what I work at on a daily basis.

IMG_2174

Neasa, first thing in the morning – full of smiles.