Category: development

Denna Jones’ Locker?

I recently worked with Jon Tan to build a site for Denna Jones, a multi-talented consultant and writer around architecture and culture. We took a somewhat new direction with her site; all of her content comes in from Web services, such as Flickr, Tumblr and Twitter. I’m going to run through how we used Flickr to manage the majority of the site’s imagery and even some of its text.

A bit of background

From the outset, the idea was to focus on content and make it as easy as possible for Denna to update her site. We gathered content into the site from the various services Denna uses (Flickr, Tumblr, Twitter, Ma.gnolia, Upcoming), removing the need for a conventional content management system.

If you want to read more about the general idea, there’s an overview of how Denna’s site was designed and built in the colophon on dennajones.com and Jon Tan gives more detail on the concept and visual design on his blog.

Denna’s site was a fun project to work on because it was a bit of an experiment. (Denna obviously enjoyed it too!) Anyone who knows me well will know that I like to play with things; I break them, find out how they work, and then piece it all back together again. I’m an engineer. Playing with a new idea gets the creative juices flowing in Jon Tan and is a catalyst for my more technical sleight of hand. (It often feels like magic, even to me!) Now, that’s always a good place to start a project! So, it wasn’t exactly the watery doom you might have expected from the title of this entry, but then my clever play on words wouldn’t have worked; the apprehensive thalassophobic among you can breathe a sigh of relief.

Fostering the friendship with Flickr

Denna was already making good use of Web services, particularly Flickr. It made sense to lasso these elements from the sea of Web services and turn her site into a treasure trove of content, helping it to reflect the very essence of what Denna is and does. So, we jacked in the usual content management system approach in favour of outsourcing everything.

Denna finds Flickr a great tool for inspiring her work, but she also uses it to document her projects and share snippets of her day-to-day life. She’d modestly tell you that she’s no photographer, but she has a strong sense of the spirit of a place and is able to capture an environment, a building or room, even intricate details, in her own unique way.

We wanted to put Denna’s photos to work. Beyond simply including the photos in the site, they formed an integral part of its design and functionality. I’ll cover some of the mechanics involved in a moment, but first I’ll run you through the areas of the site where Flickr was used:

The masthead imagery

The masthead images throughout the site are pulled from Flickr using a photo tag search. All Denna has to do to add a new masthead image to her site is tag her photos; the site does the rest. On the home page, you can flick through the full series of masthead photos.

Denna’s work portfolio

We had originally collected the projects together into one set where each photo represented a project and its description introduced the project. We soon realised that we needed sets for each project, so that Denna could easily group together several photos for each project.

This is where Flickr’s photo collections came in useful. The photos and text descriptions used for Denna’s work portfolio all live inside a specific collection on Denna’s Flickr account. Each set represents a project and the set’s description is the project’s introductory text. Visitors are invited to see more photos of each project on Flickr.

The key photo for each project set is the one used to represent that project in the portfolio, so Denna can easily change that too from Flickr’s interface. Denna’s introductory text on the work page (to the side) is also pulled from Flickr; it’s the collection description.

Denna’s footer photo

As a little added extra, the photo of Denna used in the foot of each page is her “buddy icon” on Flickr; it’s just rather nice for the two to correlate. Again, this is simply requested from Flickr using the Flickr API, so if Denna changes her buddy icon on Flickr, the foot of her site gets updated as well.

The mechanics

PHP does the hard work on Denna’s site. To be more accurate, the CakePHP framework does a lot of the hard work with a little help from some friends. One particular friend is the rather wonderful phpFlickr class, which is a wrapper for the Flickr API and makes it really straightforward to liaise with Flickr from within PHP via the Flickr API.

Those of you who have worked with the Flickr API may already be wondering how I used Flickr’s photo collections when they are not yet supported in the API. As Jon mentioned in his entry about Denna’s site, I initially had to hack tags support for Tumblr, which used cURL to scrape the necessary data directly from their site. After emailing support about the fact that you could add tags to posts but do absolutely nothing with them, the Tumblr crew eventually exposed tags via their API. In a similar way, we had to work around the missing collections features in the Flickr API as well, which I may have to cover in a separate entry. Flickr collection support been discussed by users for some time, and while it looks like a solution may be on its way, it’s not yet seen the light of fibreoptic cable.

Denna’s site also uses PHP to resize photos from Flickr as necessary and then cache the images on the server. To keep the site up to date, the cache is flushed every night and repopulated, but it can also be manually refreshed by Denna. The number of masthead images pulled from Flickr is capped off to a limit. This is so that the server doesn’t have to go processing every image that meets our photo tag search, saving the server from heavy processing tasks and potentially bringing the site down.

The masthead image on the home page is rotated when you hit “next”. This is achieved using PHP sessions to remember the current image and some cache control to switch to the next image. As a progressive enhancement, a JavaScript image rotator takes over control of this feature so that a page reload is not necessary. To speed up development, my framework friend this time was jQuery. Other pages feature randomly selected masthead images on each page load.

Until next time

That’s about it for now. I hope you’ve found this little commentary on how we put together Denna’s site interesting.

JavaScript, abbr and Internet Explorer

The other day I wrote about how screen readers handle abbreviations. I mentioned the lack of support for the abbr element in Internet Explorer (IE 6 and below) and how it may have led people to believe that screen readers working with Internet Explorer don’t support it either. I went on to explain that modern screen readers do support both abbr and acronym elements, but perhaps not in the way that developers expect.

I’ve been doing a bit of poking around and it seems that abbr elements have been available in Internet Explorer’s Document Object Model (DOM) for some time, which I didn’t realise. I’ve been able to access the title attributes of abbr elements as far back IE 5.01. However, what we haven’t been able to do is style these elements with CSS or access their child nodes (i.e. their content) with JavaScript; that is until IE 7 introduced proper abbr support.

There have been a few ways to force IE 5 and 6 to support abbr; from using extra markup, through hacking it with JavaScript, to using the html namespace to trick the browser. Now it seems there’s been a really simple JavaScript solution that’s lain dormant for about four years.

A simple JavaScript fix using createElement()

Thanks to a tip from Sjoerd Visscher, Jeremy Keith observed that we can con Internet Explorer into understanding abbr and persuade it to recognise the element as a proper part of its DOM.

So, armed with this new knowledge from Sjoerd and Jeremy, I ran another test using this createElement() JavaScript fix. The results show that IE 5.5 and IE 6 will add abbr to the DOM properly if you add this at the beginning of your JavaScript:

document.createElement("abbr");

You can style it and it even displays the title attribute as a tool tip for you! Lovely! Internet Explorer 5.01 doesn’t play ball – why am I not surprised?!

Remember, we’re not doing this for the benefit of screen readers. The modern screen readers JAWS and Window-Eyes can expand the contents of abbr and acronym elements using their title attributes. All we are doing is making sure Internet Explorer itself is fully aware of abbr, enabling us to style it with CSS and manipulate it properly within the DOM. So, whether or not you want to do this is entirely based on what you do in the browser – don’t do it for the sake of assistive technology.

Further reading

PHP + Twitter + Google Calendar + SMS

Working on a little side project, I wanted to be able to add an event to Google Calendar by text message like our American friends can using GVENT. I figured I could hook my Twitter account up to Google Calendar using their APIs. I tinkered with it for a couple of hours and it works!

Background

If you don’t know about Twitter, it’s a free service that allows you to share messages with your network of friends by a number of means, which includes via text messages sent to Twitter from your mobile phone.

Twitter has this useful feature called direct messages. It enables private messaging between friends over the Twitter system, provided you and your friend are following each other on Twitter. This means that you can send a message over the system without it turning up in your public time line of messages – your ‘tweets’. You just put a letter ‘D’ and your friend’s Twitter username in front of your message and send. So, I can send a text message to Jon via Twitter like this “d jontangerine Why, I’d love tangerines by post! Thank you!”

How is this useful for adding an event to Google Calendar? Well, a short text message is all I need to add an event using Google Calendar’s really smart Quick Add feature. A script set up to periodically check for new direct messages via the Twitter API can take the body of any message and pass it to the Google Calendar API.

It’s a bit round about in implementation, but it allows you to update a Google Calendar by SMS. In fact, you could use any Twitter-ing interface (Twitter.com, Twitterific), but then you may as well Quick Add your event with Quicksilver (Mac users) or some other widget or even – shock horror – sign into the Google Calendar site!

Obviously, I’m not the only person who’s thought of hooking up the APIs; Twittercal will give you basic functionality if that’s all you need. It doesn’t allow you to post to any calendar other than your default one, which wasn’t useful for my application, and I wanted to be able to do a bit of processing in PHP before posting the event to the calendar.

Twitter to PHP

So, I can set up a new Twitter account for my application, become friends with it (!) and send messages to it. That’s a good start. Now I need a PHP script that listens in for new direct messages on my new Twitter account.

To interact with Twitter, it helps to be familiar with the Twitter API. You might also find a wrapper for the Twitter API useful. It’s not entirely necessary, but will save you a bit of time. Here’s one David Billingham made earlier: Twitter PHP Library (Ver 0.1). It’s not complete, but it’s a good start.

I’ll not use the library for now so that I can show you how to talk to Twitter using our friendly PHP CURL functions.

  1. Set yourself up with an account for your application. Follow the new account and make sure it’s following you as well. Direct messages won’t work unless the feeling’s mutual!
  2. Grab the code below and save it to a new file on your Web server, e.g. “tcal.php”. The code is commented to guide you through what it does.
  3. Set TWITTER_CREDENTIALS to your application’s Twitter username and password.
  4. Make sure you have some direct messages sent to the Twitter account. Try sending it a test message, something like “d yourapp Smashing Pumpkins in The O2 Arena on Saturday at 7pm”.
  5. Run the script and you should see your messages in XML.
<?php

// Define credentials for the Twitter account define('TWITTER_CREDENTIALS', 'twitter_username:twitter_password');
// Set up CURL with the Twitter URL and some options $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://twitter.com/direct_messages.xml'); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2); curl_setopt($ch, CURLOPT_VERBOSE, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// Twitter uses HTTP authentication, so tell CURL to send our Twitter account details curl_setopt($ch, CURLOPT_USERPWD, TWITTER_CREDENTIALS);
// Execute the curl process and then close $data = curl_exec($ch); curl_close($ch);
// If nothing went wrong, we should now have some XML data echo '<pre>'.htmlentities(print_r($data, true)).'</pre>';
?>

Using a library obviously tidies things up and should provide some built-in error checking and return an XML object to us. You can use the library I mentioned earlier, but you’ll need make sure you add your Twitter account details to the class. We end up with this:

<?php

// The Twitter library require_once 'twitter.class.php';
// Create a new Twitter object and query for direct messages $twitter = new Twitter(); $direct_messages = $twitter->directMessages();
// We should now have some XML data in an object echo '<pre>'.htmlentities(print_r($direct_messages, true)).'</pre>';
?>

Okay, now we have a nice XML object to work with.

Google Calendar from PHP

Now we’re tuned into Twitter, we want to send any messages on to Google Calendar.

The Google Calendar API is a Google data (GData) API. The easiest way to interact with Google Calendar in PHP is with Zend’s standalone GData client library. This library also comes as part of the Zend Framework, but unless your application uses the framework already, it’s a bit of a sledgehammer!

The GData client library needs PHP version 5.1.4 or higher. If you want PHP 4 compatibility, this might make life a bit harder for you. PHP 4 coming to the end of its life and support for it ceased at the end of 2007. If you’re not yet using PHP 5, I strongly suggest your developments move towards using it.

After downloading the client library and installing the ‘Zend’ directory somewhere useful in your application, you can start to use it! Try adding this chunk of code in place of where we echoed the XML data from Twitter. Again, the comments tell you what’s going on.

<?php

// Define credentials for the Google Calendar account define('GCAL_USER', 'google_user@example.com'); define('GCAL_PASS', 'google_password');
// If you need to target a specific calendar, uncomment and enter // its calendar ID here. The calendar ID is available on the // calendar settings page, next to the Calendar Address buttons. //define('GCAL_ID', '[hash]@group.calendar.google.com');
// Include Zend GData client library require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Gdata'); Zend_Loader::loadClass('Zend_Gdata_ClientLogin'); Zend_Loader::loadClass('Zend_Gdata_Calendar');
// Get Google Calendar service name (predefined service name for calendar) $service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
// Authenticate with Google Calendar $client = Zend_Gdata_ClientLogin::getHttpClient(GCAL_USER, GCAL_PASS, $service);
// Iterate through the direct messages we got from Twitter foreach ($direct_messages as $dm) { $message = (string)$dm->text;
// If you echo the message body here, you'll notice that the // extraneous text is removed from its beginning for us //echo $message;continue;
// You could do some processing here if you wanted; // e.g. allow only direct messages from certain users detect // a particular flag or message format for processing
// Create a new Google Calendar object $calendar = new Zend_Gdata_Calendar($client);
// Create a new event $event = $calendar->newEventEntry();
// Make it a Quick Add event $event->quickAdd = $calendar->newQuickAdd('true');
// Add our message as the event content $event->content = $calendar->newContent($message);
// Send the new event to be added to Google Calendar if (defined('GCAL_ID')) { $newEvent = $calendar->insertEvent($event, 'http://www.google.com/calendar/feeds/'.GCAL_ID.'/private/full'); } else { $newEvent = $calendar->insertEvent($event); }
// Play nice and ensure there's a wait between calls sleep(1); }
?>

Running that should post your direct message on to Google Calendar and, provided Google Calendar understood your message, a new event will be added.

Once we’ve used the message, we don’t want it to get picked up and processed when the script is next run. We can delete a message using the Twitter API, but we’ll need to add something to the Twitter library we’ve been using. The following function can be added to the Twitter class to allow us to delete a direct message using its ID. I put this in after the other direct message functions.

<?php	// ...

/** * Deletes a direct message */ function deleteDirectMessage($id) { if (!is_numeric($id)) { return false; } $request = 'http://twitter.com/direct_messages/destroy/'.$id.'.xml'; return $this->process($request); }
// ... ?>

Now we just call this new method of the Twitter class to delete the message once we’re done with it.

<?php	// ...

// Send the new event to be added to Google Calendar if (defined('GCAL_ID')) { $newEvent = $calendar->insertEvent($event, 'http://www.google.com/calendar/feeds/'.GCAL_ID.'/private/full'); } else { $newEvent = $calendar->insertEvent($event); }
// Delete the direct message $twitter->deleteDirectMessage((integer)$dm->id);

// Play nice and ensure there's a wait between calls sleep(1); }
?>

And we’re done! All that’s left to do it set up a cron job on a server to run our script periodically.

I advise not running the script too often – bombarding the Twitter servers. I set mine to run every 20 minutes, but from what I’ve read you should be able to run it every minute without any trouble. If you’re going to be doing anything particularly intensive, you should probably contact Twitter first.

Here’s the code in full. Enjoy!

<?php

// Define credentials for the Google Calendar account define('GCAL_USER', 'google_user@example.com'); define('GCAL_PASS', 'google_password');
// If you need to target a specific calendar, uncomment and enter // its calendar ID here. The calendar ID is available on the // calendar settings page, next to the Calendar Address buttons. //define('GCAL_ID', '[hash]@group.calendar.google.com');
// The Twitter library // Make sure you've set your Twitter username and password and // added the function deleteDirectMessage() require_once 'twitter.class.php';
// Include Zend GData client library require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Gdata'); Zend_Loader::loadClass('Zend_Gdata_ClientLogin'); Zend_Loader::loadClass('Zend_Gdata_Calendar');
// Create a new Twitter object and query for direct messages $twitter = new Twitter(); $direct_messages = $twitter->directMessages();
// Get Google Calendar service name (predefined service name for calendar) $service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
// Authenticate with Google Calendar $client = Zend_Gdata_ClientLogin::getHttpClient(GCAL_USER, GCAL_PASS, $service);
// Iterate through the direct messages we got from Twitter foreach ($direct_messages as $dm) { $message = (string)$dm->text;
// If you echo the message body here, you'll notice that the // extraneous text is removed from its beginning for us //echo $message;continue;
// You could do some processing here if you wanted; // e.g. allow only direct messages from certain users detect // a particular flag or message format for processing
// Create a new Google Calendar object $calendar = new Zend_Gdata_Calendar($client);
// Create a new event $event = $calendar->newEventEntry();
// Make it a Quick Add event $event->quickAdd = $calendar->newQuickAdd('true');
// Add our message as the event content $event->content = $calendar->newContent($message);
// Send the new event to be added to Google Calendar if (defined('GCAL_ID')) { $newEvent = $calendar->insertEvent($event, 'http://www.google.com/calendar/feeds/'.GCAL_ID.'/private/full'); } else { $newEvent = $calendar->insertEvent($event); }
// Delete the direct message $twitter->deleteDirectMessage((integer)$dm->id);
// Play nice and ensure there's a wait between calls sleep(1); }
?>

Related

Quick Tip: speedier PHP/CSS commenting

Continuing in the vein of “the obvious ones are often overlooked,” here’s another quick productivity tip!

When you want to type a comment in a PHP or CSS file, speed things up a little by using the / and * keys on the numeric keypad. On (most) laptops, hold down the Function (Fn) key to access the overlaid numeric keypad.

Back when I used to use a full-size keyboard, I would use the / and * keys on the numeric keypad to quickly insert comments into my PHP or CSS. It was so much quicker than using / and Shift+8 keystrokes.

When problems with RSI last year encouraged me to switch to using a compact keyboard, I missed that shortcut. However, I quickly realised that I could hold down the Function (Fn) key on my new keyboard to quickly access the overlaid numeric keypad.

I recently bought myself a MacBook Pro and have just figured out that I can do the same thing. Holding down Function (Fn) allows me to access the numeric keypad and my quick way of typing comments. Yay!

feed://Sage…

I recently ran into a couple of problems with the wonderful Sage extension for Firefox where it wouldn’t parse some of my feeds. I figured the feed URLs must have been updated, so I went to the sites and grabbed fresh RSS URLs. Some of the feed URLs had indeed been updated, but some of them still wouldn’t work in Sage.

As it turns out, there were two problems occurring:

  1. The URLs had the “feed” protocol tacked on the front.
  2. One feed was actually invalid. Despite it validating with feedvalidator.org and the W3C feed validator, the Firefox XML parser failed on it, as did Sage.

The Feed Protocol

Once I had actually figured out what was going wrong here, it was simple enough to fix! Simply remove the feed protocol from the start of the URL to stop Sage choking on it.

Marvellous! But it did leave me wondering what use the feed protocol is to us, and whether feed aggregators should be expected to deal with the protocol just in case somebody uses it. I submitted a Sage bug report about the feed protocol all the same, so I’m hoping Sage will get updated with a fix soon enough.

Some time ago, I began to encounter more and more feed URLs using the protocol (probably as a result of WordPress including it by default at the start of its feed URLs), so I read up on it a little. Without wanting to come into what is an old issue, I do wonder whether it is at all useful any longer, especially when so many of our beloved browsers can now handle feed subscription. We could send commands to applications aware of the feed protocol as we can do with mailto, but what useful commands would we send? Perhaps I’m stuck thinking about how these might be useful to aggregators and not a wider application, but surely aggregators just need to know the location of a feed? With that in mind, it just seems redundant information to me, although it could be useful in identifying a feed without needing to use MIME types.

Named HTML Entities in RSS

The second problem wasn’t so obvious. This time, Firefox was choking on the feed because it found an undefined entity in the RSS – &hellip; – a horizontal ellipsis. I wasn’t sure why that’d break the RSS, so I did a little digging.

Named HTML Entities in RSS

A problem with feeds not working in Sage alerted me to how character references should be used in RSS feeds.

Despite validating with feedvalidator.org and the W3C feed validator, the Firefox XML parser failed on this one feed, as did Sage. Firefox found an undefined entity in the RSS – &hellip; – a horizontal ellipsis. I wasn’t sure why that’d break the RSS, so I went digging a little…

Naughty names

RSS doesn’t include an XML schema, which means that a named entity such as &hellip; is unlikely to mean anything to a feed reader. &hellip; is fine in an XHTML document because it generally has a doctype and a schema.

So if you use data from a content management system to generate RSS, you need to ensure that all named entities are converted into numeric character references. Edit: And as Robert Wellock points out in the comments below, it’s advisable to stick to using numeric character references when using XHTML anyway.

Numeric character references

As far as I know, numeric character references are generally better supported than named entities. I tend to use numeric character references anyway when I code, as I’m sad and have a bunch of the numbers committed to memory after years of usage – that’s a scary thought!

Having said that, I do have Dave Child’s HTML Character Entities Cheat Sheet stuck up in my office footnote 1, with a few I’ve added myself, including:

  • horizontal ellipsis (&#8230;)
  • en dash and em dash (&#8211; and &#8212;)
  • left and right double quote (&#8220; and &#8221;)
  • left and right single quote (&#8216; and &#8217;)

Of course, the real geeks among us will look things up in the full list of character references in the HTML 4 schema!

Other Resources

More useful info at:

Footnotes

  1. Dave, I can’t really afford anything for you off Amazon at the moment, but I certainly owe you a drink or two by now! Back to footnote 1 source