I was recently asked to add a simple twitter feed to a Drupal 5 site I built a few years ago. You know the sort of thing - a block in the sidebar with the 5 most recent tweets from the organisation's twitter account. Unfortunately, this turned out to be one of those requests that sounds like it's going to be really easy, but is not (especially on Drupal 5). I came up with a solution which does the job quite nicely, but it's a bit of a hack; I wouldn't do it like this on a new site - but then of course I wouldn't be using Drupal 5.
There are several twitter modules for Drupal, as you'd expect. The twitter module does have a Drupal 5 version, but it's a minimal dev release and doesn't seem to offer much of the functionality of the other releases. The other module which sounded promising was twitter pull but this has no D5 release at all. I had a think about doing a backport, but this was supposed to be a quick favour and with the imminent release of D7 (ahem!), working on Drupal 5 code hardly seems like an investment in the future.
Then I checked, and found that twitter provides RSS feeds for users' timelines, and obviously D5 has a perfectly functional RSS aggregator module built-in. The RSS feed looks something like this:
<a href="http://twitter.com/statuses/user_timeline/30355784.rss
">http://twitter.com/statuses/user_timeline/30355784.rss
[/geshifilter-text]
...so I set this up as a feed (admin/content/aggregator), and this automatically provided a block. This works okay, but the problem is that each item ends up like this:
[[html]]
mcdruid_co_uk: Drupal Quiz module: "action to be preformed after a user has completed Quiz: Ban IP address of current user" - seems a bit extreme, shirley?
[[/html]]
...so we end up with the entire tweet being a link, pointing at a page displaying just that one tweet. This is not exactly what I was hoping for.
So here's the slightly nasty hack; like all well-written Drupal modules, aggregator runs its output through a theme function, which can be overridden:
<?php function theme_aggregator_block_item($item, $feed = 0) { global $user; if ($user->uid && module_exists('blog') && user_access('edit own blog')) { if ($image = theme('image', 'misc/blog.png', t('blog it'), t('blog it'))) { $output .= '<div class="icon">'. l($image, 'node/add/blog', array('title' => t('Comment on this news item in your personal blog.'), 'class' => 'blog-it'), "iid=$item->iid", NULL, FALSE, TRUE) .'</div>'; } } // Display the external link to the item. $output .= '<a href="'. check_url($item->link) .'">'. check_plain($item->title) ."</a>\n"; return $output; } ?>
We wouldn't necessarily want to mess with the output of feeds other than this twitter one, so we need a way of identifying items from this feed. I noticed that twitter's RSS feed prepends the twitter username to each post, so we can use this to identify them.
I borrowed a function from the twitter module which deals with twitter @usernames and #hashtags. After that, running the tweet through the default input filter strips out potential nasties, and also converts URLs to links (assuming you have this switched on in your default input filter). The item includes a timestamp, so I used that to append a date/time. I didn't want the blog it stuff, so my theme code (in template.php) ended up looking like this:
define('ORGNAME_TWITTER_USERNAME', 'orgname'); /** * theme aggregator items */ function mytheme_aggregator_block_item($item, $feed = 0) { // horrible hack, but catch items which are part of the twitter feed $twitter_username = variable_get('orgname_twitter_username', ORGNAME_TWITTER_USERNAME); $tweet_prefix = "$twitter_username: "; if (strpos($item->title, $tweet_prefix) === 0) { // chop the username part from the start $tweet = substr($item->title, strlen($tweet_prefix)); // @usernames $tweet = _mytheme_twitter_link_filter($tweet); // #hashtags $tweet = _mytheme_twitter_link_filter($tweet, '#', '<a href="http://search.twitter.com/search?q=%23'">http://search.twitter.com/search?q=%23'</a>); // add date $tweet .= ' <span class="date">(' . format_date($item->timestamp) . ')</span>'; // filter $tweet = check_markup($tweet); $output = trim($tweet); } else { // default implementation $output = '<a href="'. check_url($item->link) .'">'. check_plain($item->title) ."</a>\n"; } return $output; } /** * stolen from twitter module (for D6) * * This helper function converts Twitter-style @usernames and #hashtags into * actual links. */ function _mytheme_twitter_link_filter($text, $prefix = '@', $destination = '<a href="http://twitter.com/'">http://twitter.com/'</a>) { $matches = array( '/\>' . $prefix . '([a-z0-9_]{0,15})/i', '/^' . $prefix . '([a-z0-9_]{0,15})/i', '/(\s+)' . $prefix . '([a-z0-9_]{0,15})/i', ); $replacements = array( '><a href="' . $destination . '${1}">' . $prefix . '${1}</a>', '<a href="' . $destination . '${1}">' . $prefix . '${1}</a>', '${1}<a href="' . $destination . '${2}">' . $prefix . '${2}</a>', ); return preg_replace($matches, $replacements, $text); }
The result is nicely filtered and formatted tweets, with @usernames, #hashtags and URLs all converted to links.
If you don't like the way the aggregator module provides the block for the feed, you can always take what aggregator outputs and manipulate it with your own module code, e.g.:
// get the block content from aggregator module $block = module_invoke('aggregator', 'block', 'view', 'feed-1'); $block_content .= $block['content'];
It'll do the job until the site's upgraded to a newer version of Drupal.
Comments
Thats great - worked a treat
Thats great - worked a treat thanks!
I printed the feed block out in my page.tpl file and used the following:
$block = module_invoke('aggregator', 'block', 'view', 'feed-1');
Many thanks, Kevin