There are no comments yet...Kick things off by filling out the form below.
Community Reactions Extension for Graffiti
As you probably know, Graffiti 1.0 Beta 1 doesn't have support for trackbacks and pingbacks. Scott had written a post regarding the exclusion of this feature and as he said trackbacks will be included in Beta 2 as an optional feature.
Even though I agree with Scott on the fact that trackbacks aren't as useful as the past but we still can't ignore them because we really need a way to show relationships between different pages on the web.
In this post I write about the necessity of having such a feature to connect two pages that are linked together and the process that I followed to create an extension for Graffiti that collects incoming links for a specific post from search engines as a replacement solution for trackbacks and pingbacks.
Background
While trackbacks are an excellent way to connect pages on the web but there are two negative points about them:
- They bring spam to the sites and blogs and can open more holes for spammers.
- They're not 100% trustable because there are always more pages that may be connected to a specific page. Sometimes we receive incoming links from sites and blogs that don't support trackbacks and sometimes trackbacks don't work because of an error on the source site or the destination site.
As long as we go forward in the time, search engines have gotten better and we're seeing some professional search engines designed specifically for blogs. Some people have decided to use search engines as a replacement for trackbacks and pingbacks because they solve the abovementioned negative points to some extent:
- No one can add anything to your site and you install something to collect incoming links from search engines so only real links to your site will be shown.
- As long as a page is indexed by a search engine you can show it as a related link and temporary problems won't cause difficulties. Moreover, neither you nor the source owner site need to add a specific feature to your site (regardless of the plug-in or extension that you need to install on your site to collect links).
It's over a month that I'm running on Graffiti 1.0 Beta 1 and the lack of built-in support for such a feature (as a trackback or search engine link collector) was one of the main negative points with the product for me. You know that I have a serious passion on the community and would like to show related content to my posts to let my visitors track them and get more information.
Andrew Tobin had written an extension for Graffiti that shows the links count for incoming links by fetching the data from Technorati and providing a link to Technorati search results page. But I needed something different because I wanted to show all incoming links immediately under my post content. Moreover Technorati doesn't index all links and I thought it's better to merge the results from multiple search engines to get a better result.
Overview
Thinking about the idea, I finally decided to write my own implementation for it as an extension for Graffiti.
This extension is nothing more than a custom Chalk extension that follows some simple steps to collect links from search engines.
I named this extension Community Reactions because it collects community reactions for a post. Community Reactions extension fetches data from RSS feed for search results for a specific post on Google blog search and Technorati and merges these results and removes duplicate items to have a clean list of reactions then returns this list.
Chalk extensions are easy to write (as described here) and you just need to create a class that has a Chalk attribute and also has public methods that return string value of the HTML code that should be displayed for an extension.
I'll describe the process of implementation in a moment. I'm going to use this extension on my site for a while to see how it works and will decide later if I can keep it here or not.
The Implementation
The implementation of Community Reactions extension was a straightforward process (at least it was easier than writing this post and took less time than it!). The first step was to create a new class library project and add a public class to it then adding a Chalk attribute to the class.
Before moving on to the actual implementation, I have to say that I created a simple helper class and named it Reaction that represents a community reaction by three properties for its title, URL and publish date and has a simple code as you see:
using System;
using System.Collections.Generic;
using System.Text;
namespace Graffiti.Reactions
{
internal class Reaction
{
public string Title { get; set; }
public string Url { get; set; }
public DateTime PubDate { get; set; }
public Reaction()
{
}
public Reaction(string title, string url, DateTime pubDate)
{
this.Title = title;
this.Url = url;
this.PubDate = pubDate;
}
}
}
The next step was to implement the main logic in the Reactions class. There is a single public method called GetReactions which gets a Graffiti.Core.Post object and a string value of the text that should be displayed if there wasn't any incoming link. This method returns the string value of the HTML code that should be shown.
public string GetReactions(Post post, string notFoundText)
{
List<Reaction> initialReactions = new List<Reaction>();
try
{
// Add Google results
initialReactions.AddRange(GetGoogleReactions(post));
// Add Technorati results
initialReactions.AddRange(GetTechnorariReactions(post));
}
catch (Exception ex)
{
Log.Error("Error in Community Reactions Extension", ex.Source);
}
// Remove duplicate items and sort final items by date
List<Reaction> finalReactions = new List<Reaction>();
finalReactions = FilterItems(initialReactions);
// Generate the output and return it
return GetHtml(finalReactions, notFoundText);
}
As you see there are a few steps to fetch data.
The first step is to call appropriate methods to get a list of Reaction items for incoming links from Google and Technorati. GetGoogleReactions and GetTechnoratiReactions methods do this and both have similar implementations.
private List<Reaction> GetGoogleReactions(Post post)
{
string rssUrl = string.Format(googleRssUrl, new Macros().FullUrl(post.Url));
List<Reaction> results = new List<Reaction>();
try
{
Feed feed = new Feed();
feed = FeedManager.GetFeed(rssUrl);
if (feed == null)
{
feed.Url = rssUrl;
feed.RequestInterval = feedUpdateInterval;
FeedManager.AddFeed(feed);
FeedManager.UpdateFeed(feed);
feed = FeedManager.GetFeed(rssUrl);
}
RssChannel channel = feed.Document.Channel;
foreach (RssItem item in channel.Items)
{
Reaction reaction = new Reaction(item.Title, item.Link,
DateTime.Parse(item.PubDate));
results.Add(reaction);
}
}
catch (Exception ex)
{
Log.Error("Error in Community Reactions Extension", ex.Source);
}
return results;
}
private List<Reaction> GetTechnorariReactions(Post post)
{
string rssUrl = string.Format(technoratiRssUrl, new Macros().FullUrl(post.Url));
List<Reaction> results = new List<Reaction>();
try
{
Feed feed = new Feed();
feed = FeedManager.GetFeed(rssUrl);
if (feed == null)
{
feed.Url = rssUrl;
feed.RequestInterval = feedUpdateInterval;
FeedManager.AddFeed(feed);
FeedManager.UpdateFeed(feed);
feed = FeedManager.GetFeed(rssUrl);
}
RssChannel channel = feed.Document.Channel;
foreach (RssItem item in channel.Items)
{
Reaction reaction = new Reaction(item.Title, item.Link,
DateTime.Parse(item.PubDate));
results.Add(reaction);
}
}
catch (Exception ex)
{
Log.Error("Error in Community Reactions Extension", ex.Source);
}
return results;
}
Graffiti applies RSS Toolkit component in order to provide a built-in feature to fetch data from feeds. It adds a list of feeds to its database and updates their data on a regular basis. The code is easy to read and understand but I have to note that I used FeedManager.GetFeed method to get an instance of the Feed object. If there wasn't any feed available then I had to add it to the system myself and then update its data. After all these steps I wrote a simple loop to iterate through feed items and add their data to results.
The next step was to filter items by removing duplicate links and sorting them by date. This was the job of the FilterItems method by getting the help of the ContainsUrl function.
private List<Reaction> FilterItems(List<Reaction> initialReactions)
{
List<Reaction> finalResults = new List<Reaction>();
foreach (Reaction item in initialReactions)
{
if (!ContainsUrl(finalResults, item.Url))
finalResults.Add(item);
}
finalResults.Sort(delegate(Reaction reaction1, Reaction reaction2)
{
return Comparer<DateTime>.Default.Compare(reaction2.PubDate, reaction1.PubDate);
});
return finalResults;
}
private bool ContainsUrl(List<Reaction> reactions, string url)
{
foreach (Reaction item in reactions)
{
if (item.Url == url)
return true;
}
return false;
}
And obviously the last step was to generate the output as a list which is the job of the GetHtml function.
private string GetHtml(List<Reaction> finalReactions, string notFoundText)
{
if (finalReactions.Count > 0)
{
StringBuilder generator = new StringBuilder("<ul>");
foreach (Reaction reaction in finalReactions)
{
generator.AppendFormat("<li><a href='{0}' title='{1}'>{2}</a></li>",
reaction.Url, reaction.PubDate.ToLongDateString(), reaction.Title);
}
generator.Append("</ul>");
return generator.ToString();
}
else
{
return notFoundText;
}
}
This was all the implementation. however there are a few points to mention. There were three constant values for the RSS feed URLs and the request interval to update feed data that you can modify for your own.
As you saw it's very easy to add other search engines or replace these current engines but I think Google and Technorati would be enough to collect all community reactions. Using more search engines can provide an overhead on the server because it has to request data for each post and each engine.
Download
You can download the binary and source code for Community Reactions extension from here.
How to Use
Using a Chalk extension is as easy as using the Chalk, itself. You just need to deploy the binary file (Graffiti.Reactions.dll) to bin folder of your site and modify your theme (most likely the post.view file) to use it.
After that you to add the appropriate header markup for the section and a line of code like what you see here:
$reactions.GetReactions($post, "Could not find any community reaction.")
The result is very nice!
As the final note please take care about your database while using this extension. Since it does store the feed data in database and updates it on a regular basis you may end up with larger databases. If the database size and its performance is a concern for you then try to test it before deploying to the production. The more posts you have, the more storage size that you need to use and more requests from your server to search engines! If you want to use other storage systems, you can write alternative implementation to store feed data such as file system but this isn't an integrated implementation for Graffiti, though!
[advertisement] Axosoft OnTime 2008 is four developer tools in one: bug tracking, project wiki, feature management, and help desk. It manages your development process so developers can focus on coding. Installed or Hosted – Free Single-user license -- Free 30-day team trial.
No Comments : 01.07.08
