Building a TweetThis Extension with Automatic Bit.ly for Graffiti CMS
A tutorial on how to build extensions for Graffiti CMS. Here, I walk through how to build an extension in C# / .NET that lets your readers tweet your blog posts – complete with title and automatically generated bit.ly URL – with one click.
I’ve recently joined Twitter, and so far I’m having a lot of fun with it. We’ll see how long that lasts. ;)
One of the reason I joined is that I’ve started to note some significant traffic from some of my posts being “tweeted,” (twitted? twatted? sorry folks, I’m not really down with the lingo yet) and now that I’m tweeting, I’ve been sharing posts I find interesting as well. So, in the spirit of sharing, and to make it a even easier for readers to share my, um, wisdom(?), I thought I’d try implementing a little one-click “Tweet this” link that would appear at the end of each of my blog posts.
The Problem
Twitter lets you update your status really easily by pinging it with a URL in the following format:
http://twitter.com/home/?status=I am the great and powerful Oz
All the stuff that comes after the ‘=’ sign becomes the tweet. So, for example, if someone reading this very article wanted to tweet it (hint hint), providing an HTML link like the following would do the trick:
Tweet this!
Here, the tweet includes my Twitter ID, the title of the post, and the URL, but there’s a BIG problem – this tweet is 187 characters, 40 over the miserly Twitter limit of 140. Trying to dash long URLs like this will usually bust your tweet (see, I’m getting some of the lingo). This is why link-shortening services are an essential adjunct to Twitter.
Tweet this!
Ah, that’s better. By using the bit.ly service, I’ve just shortened the URL from 96 characters to 19 characters, so now my tweet is 110 characters total. There are lots of link shortening services out there, but bit.ly is unique in that you can create an account with them, through which they allow you to easily track clicks to your shortened URLs in real time, as well as view referrer data (in case your URL is posted to another site), geo-location data, and even see Twitter conversations that include your URL. Very, very cool indeed.
Hmm, on second thought, we probably oughta URL-encode that. Better safe than sorry, dontcha know.
Tweet this!
So, in order to provide some convenient “Tweet this” goodness, I simply need to append the HTML above to the end of this blog post. Oh, and then go back and do likewise to everything I’ve already published.
Yeah, right. I could do this by hand, but being a programmer, I’m far too lazy for that. We’d want to implement this so that the correct tweet link renders automatically, right? That means we need a way to submit the long URL to bit.ly and get a short URL for it (without human intervention), construct the tweet, then URL encode it, so that we can build the proper HTML on the server. Then, we need to get that HTML to render on our post pages.
The Design
As you can see, I’m currently running this blog on Graffiti CMS from Telligent Systems. The great thing about Graffiti is that extending its functionality is a fairly straightforward process if you know how. The bad thing about Graffiti is that their developer documentation sucks big time. Not to worry though, because I’m going to walk you through everything you need to know. If you don’t really care to peek beneath the covers with me, you can just skip to the end and download the extension, along with the instructions to implement it on your own blog.
Graffiti uses the Chalk templating language to render themes (Graffiti… Chalk… get it?). “Chalk” is basically Telligent’s flavor of NVelocity. The Chalk syntax is exactly the same as that of NVelocity, with some proprietary stuff thrown in.
Each Graffiti theme comes with a number of *.view file templates. These files are located in the /files/themes/YourThemeName folder. The template that is used to render posts is called post.view.
If you open post.view, you’ll see it looks a lot like HTML, but not quite. There are some strange-looking tags in there that don’t look like HTML at all. That’s Chalk.
$post.Title
$post.Body
The highlighted lines above contain Chalk properties. The $post part is a representation of a Graffiti Post object. The $post.Url property renders the relative URL of the post wherever it appears. The $post.Title property renders the title, and $post.Body… well, I’ll let you guess. ;) So, the code snippet above simply renders an level-2 header with a link for the post’s title, followed by the body of the post.
There are Chalk methods as well, that can accept Chalk objects, properties and other elements as arguments.
Tagged as: $macros.TagList($post.TagList,"")
The above snippet shows a Chalk property and an empty string being passed as arguments to the macros.TagList method. This renders a div containing a comma-delimited list of tags for a post.
As I said, Graffiti is extendable, which means you can make up your own Chalk objects, methods, and properties. For example, we could do something like this:
$Promote.TweetThis($post)
In other words, we can create a Promote class with a TweetThis method that accepts a Post, extracts some information from it, and renders a “Tweet this” link for us. The one we’ll create will actually be a bit more functional than this. Take a deep breath. Ready? Let’s do this.
The Solution
There are three basic steps to extending Chalk:
- Create a .NET class library. Be sure to add a reference to Graffiti.Core.dll to it. You may need to reference other assemblies as well, depending on what functionality you require.
- Add a ChalkAttribute decoration to the class, and give it a name. (This class cannot be static, though its methods may be. Only public methods and properties of the class are exposed.)
- Compile the project and drop the resulting .dll into the /bin folder of your blog site.
If all goes well, you’ll then be able to include your new Chalk extension(s) in your .view files.
First, create a class library and reference Graffiti.Core.dll. In our case, you’ll need to reference DataBuddy.dll too. You’ll find both of these assemblies in the /bin folder of your Graffiti download.
You’ll note from the above screenshot that there are going to be two classes in our library: Helpers.cs and Promote.cs.
In the Helpers class, we’ll build a couple of utilities that will help us derive a bit.ly URL from a given long URL.
// by Lee Dumond - http://leedumond.com namespace LD.GraffitiCMSExtensions { using System.Collections.Generic; using System.Net; using System.Text; using System.Web; using System.Web.Caching; using System.Web.Script.Serialization; ////// Provides helper utilities for extensions. /// internal static class Helpers { ////// Shortens a long URL using bit.ly. /// /// The URL to shorten. /// A bit.ly login. /// A bit.ly API key. ///A bit.ly URL that links to the provided long URL. public static string GetBitlyUrl(string longUrl, string bitlyLogin, string bitlyApiKey) { Cache cache = HttpContext.Current.Cache; string cacheKey = longUrl + "_bitly_cache"; string bitlyUrl; if (cache[cacheKey] != null) { bitlyUrl = cache[cacheKey].ToString(); } else { string jsonString = GetJsonString(string.Format( "http://api.bit.ly/shorten?version=2.0.1&longUrl={0}&login={1}&apiKey={2}", HttpUtility.UrlEncode(longUrl), bitlyLogin, bitlyApiKey)); if (jsonString != null) { JavaScriptSerializer jss = new JavaScriptSerializer(); DictionaryobjDict = (Dictionary ) jss.DeserializeObject(jsonString); string bitlyError = objDict["errorCode"].ToString(); if (bitlyError == "0") { Dictionary results = (Dictionary ) objDict["results"]; Dictionary resultsLongUrl = (Dictionary ) results[longUrl]; bitlyUrl = resultsLongUrl["shortUrl"].ToString(); cache.Insert(cacheKey, bitlyUrl); } else { bitlyUrl = longUrl; } } else { bitlyUrl = longUrl; } } return bitlyUrl; } private static string GetJsonString(string url) { try { WebClient wc = new WebClient(); byte[] data = wc.DownloadData(url); return data != null ? Encoding.ASCII.GetString(data) : null; } catch (WebException) { return null; } } } }
The bit.ly folks provide a nice REST API that allows developers to interact with their service programmatically. The API requires HTTP Basic Authentication using your bit.ly account login and API key. An API key is created for you automatically when you sign up; you can grab it right off your account page.
As you can see, the default response format is JSON. We use a private GetJsonString method to fetch the response. Then, in the GetBitlyUrl method, we deserialize the response string and cast it to a Dictionary
If by any chance the JSON response is null or the bit.ly service reports an error, we return the original (long) URL unaltered. In that case the response is not cached, so that we get another crack at retrieving the short URL on the next page hit.
The Promote class contains our actual Chalk methods.
// by Lee Dumond - http://leedumond.com namespace LD.GraffitiCMSExtensions { using System.Web; using Graffiti.Core; ////// Provides methods to promote blog posts via social networking. /// [Chalk("Promote")] public class Promote { ////// Generates HTML for tweeting a blog post, using "Tweet this" as the link text. /// /// The post to tweet. /// The poster's Twitter ID. /// A bit.ly login ID. /// A bit.ly API key corresponding to the provided bitlyLogin. ///The HTML link. public static string TweetThis( Post post, string twitterId, string bitlyLogin, string bitlyApiKey) { return TweetThis(post, twitterId, bitlyLogin, bitlyApiKey, "Tweet this"); } ////// Generates HTML for tweeting a blog post, using either an image or plain text. /// /// The post to tweet. /// The poster's Twitter ID. /// A bit.ly login ID. /// A bit.ly API key corresponding to the provided bitlyLogin. /// The absolute URL of the image to use for the link, or the text to use for the link. /// If the argument starts with "http://", it will be treated as a button URL; otherwise, it will be treated as plain text. ///The HTML link. public static string TweetThis( Post post, string twitterId, string bitlyLogin, string bitlyApiKey, string tweetButtonUrlOrLinkText) { string shortUrl = Helpers.GetBitlyUrl(new Macros().FullUrl(post.Url), bitlyLogin, bitlyApiKey); string statusString = HttpUtility.UrlEncode(string.Format("Just read @{0} {1} {2}", twitterId, post.Title, shortUrl)); string anchorContents; if (tweetButtonUrlOrLinkText.StartsWith("http://")) { anchorContents = string.Format("", tweetButtonUrlOrLinkText); } else { anchorContents = tweetButtonUrlOrLinkText; } string tweetFormatString = string.Format( "{1}", statusString, anchorContents); return tweetFormatString; } } }
You’ll see there are two overloads of the TweetThis method. The first returns a plain “Tweet this” text link. The second returns either a plain text link with custom text, or an image link, depending on the value you supply for the tweetButtonUrlOrLinkText parameter. If you supply a string beginning in “http://”, the method assumes that it’s a URL for a button image, and builds the HTML string accordingly. Otherwise, it treats the value as custom text for a plain text link.
Once you’ve done this, compile the library in release mode, grab the LD.GraffitiCMSExtensions.dll file, and drop it into the /bin folder of your production site. If you plan to use an image for the link, be sure to upload that to your site as well.
All you have left to do is to edit your post.view template. But before you do that, I highly recommend that you open the web.config file of your Graffiti site and change the appSetting shown blow to "false". This makes if much easier to test edits you make to your .view files. Just don’t forget to set it back to true once you’ve finished editing.
You can edit post.view using any method you wish. You may find it easier to work with it locally using a text editor and FTP the edited file to your production site; if so, be my guest. You can also edit it live on the server through the GraffitiCMS control panel (Presentation –> Themes –> YourThemeName –> Personalize).
Locate a position on the page where you want the button to appear (I put mine directly under the post body). Here is where you’ll invoke the Chalk method you just built.
$post.Title
$post.Body$Promote.TweetThis($post, "LeeDumond", "lee123", "R_1234567890qbcdef1234567890abcdef", "http://leedumond.com/files/themes/leestheme/i/tweetthis.gif")
As documented in the class, the parameters are in the following order: the Post object, your Twitter ID, your bit.ly login, your bit.ly API key, and a string representing the contents of the anchor tag (I’m using an image URL for mine). You don’t have any Intellisense here, so be careful to pass the arguments in the correct order.
Summary
In this blog post, you learned how to post a tweet via a URL. You learned about using the bit.ly service to create compact URLs, how to use the bit.ly REST API to generate short URLs on-the-fly, and how to parse the JSON response programmatically to get at the generated bit.ly data.
You also learned about Graffiti’s Chalk templating language, and how to invoke properties and methods in a .view template. You then learned how to extend Graffiti with your own custom Chalk extension methods.
With this knowledge, we created a custom “Tweet this” button that can appear on every post page. The result is just below. While you’re here, why not go ahead and give it a click? Come on… you know you want to. ;)
(Oh yeah, I promised you the source code, and here it is. By the way, the source also contains Chalk methods for the DotNetKicks and DZone widgets you see below.)
Subscribe to this blog for more cool content like this!
Bookmark / Share
» Similar Posts
- Open Source or Die – The *Real* Future of Graffiti?
- Defensive Programming, or Why Exception Handling Is Like Car Insurance
- If At First You Don’t Succeed - Retrying Mail Operations in .NET
» Comments
-
You might want to check out the sample plugins I posted here: simpable.com/.../twitter-and-rss
Graffiti will allow you to wire up code that fires at certain times in the application life cycle.
In this case, you could look up and cache the bit.ly url and save the extra look up to bit.ly.
Scott Watermasysk — June 11, 2009 8:42 AM -
@ScottW - Was hoping to stay YAGNI on this and avoid implementing GraffitiEvent. With the implementation above, there is still only one hit to the bit.ly service per post per application lifecycle, so the perfomance should be the same. I may look into hooking into the API a bit differently next time though. Thanks for the feedback.
Lee Dumond — June 11, 2009 12:36 PM -
That's cool, if we tweets using our own blog post. Thank you for this cool post !
Arabic CMS — August 6, 2009 7:02 AM -
good information, thank you
Auto Repair Manual — August 6, 2009 1:06 PM -
Thanks mate!Graffiti is the best CMS ever.I changed all of my sites to it
Italian Translation — August 7, 2009 6:16 PM -
Excellent information Lee. Thanks for sharing. All the best - Mike.
Italian translation by Lingua21 — September 29, 2009 8:15 AM -
Great blog, this could be the best blog I ever visited this month. Never stop to write something useful dude!
oes tsetnoc — November 13, 2009 12:51 PM -
yeah. thanks a lot. :)
solid piece of information. Happy tweeting..?
increase your traffic with klikrar — March 11, 2010 7:17 PM -
This is some good application, i new in this twiitter thing, is htis hard to implement?
Mariscos — June 15, 2010 6:01 PM
You've been kicked (a good thing) - Trackback from DotNetKicks.com
Thank you for submitting this cool story - Trackback from DotNetShoutout
Pingback from Dew Drop - June 12, 2009 | Alvin Ashcraft's Morning Dew