Click here to Skip to main content
15,879,535 members
Articles / Operating Systems / Windows

Graffiti CMS Plugin – Custom Categories for RSS Feed Items

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
25 Sep 2009Ms-PL5 min read 10.4K   3  
Graffiti CMS Plugin – Custom Categories for RSS Feed Items

I was over at CodeProject recently and saw that you can set up your account to pull in blog content as articles on their site, so I decided to try it out. They have a few different ways to retrieve the content from a blog:

  • Add a <category> element to the <channel> in your RSS feed. All posts will get pulled in to CodeProject.
  • Add a <category> element to individual <item> elements in your RSS feed to just send those items to CodeProject.
  • Add some specific HTML to each post that you want to have pulled in to CodeProject

Since this site is built on Graffiti CMS, and the default RSS feed is handled completely by Graffiti, I initially threw out the first two options, hoping that the third option would work and that would be that. Unfortunately, for whatever reason, CodeProject just couldn't find the HTML in my posts like I thought it should. Probably something to do with the encoding happening in the feed, but I have no idea for sure.

So that left me with the first two options. I didn't want to have every post go over to CodeProject, since not everything here is explicitly code related, so that threw out the first option (which is good, because it turns out that there’s no way that I could do that with Graffiti right now anyways).

It turns out that it’s pretty simple to hook into the required event in Graffiti to add this custom data to the RSS feed item, but due to the lack of documentation on creating plugins, it would have taken me a lot longer if not for the Blog Extensions plugin written by the Telligent staff. That proved to be a great example on what is needed to get the plugin to work.

Creating the Plugin

To create a plugin for Graffiti, all you really need is a single class derived from Graffiti.Core.GraffitiEvent (located in Graffiti.Core.dll). In this class, you need to override the Init function, which allows you to set up the events that you want to hook into. For this plugin, this is just a single event:

C#
public override void Init(GraffitiApplication ga) 
{ 
	ga.RssItem += new RssPostEventHandler(ga_RssItem); 
}

The RssItem event is fired whenever Graffiti is trying to render an <item> element in an RSS feed. I will go into detail for the event handler itself in just a bit, but first I wanted to cover some of the other overrides that are needed to get the plugin to function in a usable manner.

First, there’s the AddFormElements() function. This function is used to set up the form displayed when you Edit the plugin in the Graffiti Admin. In it, you create the specific Graffiti form elements that are needed to configure the plugin. In this case, there is just a single checkbox:

C#
protected override FormElementCollection AddFormElements() 
{ 
	FormElementCollection fec = new FormElementCollection(); 
	fec.Add(new CheckFormElement("enableRssCategories", "Enable Rss Categories", 
		"Allows you to specify custom &lt;category&gt; 
		elements on individual posts", false));

	return fec; 
}

There are custom elements for checkboxes, textboxes, textareas, file uploads, datetimes, lists and a WYWIWYG control. These can all be added to the configuration form. The code provided above generates a form that looks like this:

RSSExtensionsPluginForm

To get that checkbox to save, there are a few other things that needed to be added. First, I added a property to hold the value:

C#
public bool EnableRssCategories { get; set; }

Then I had to override two more functions, DataAsNameValueCollection() and SetValues():

C#
protected override System.Collections.Specialized.NameValueCollection 
	DataAsNameValueCollection() 
{ 
	NameValueCollection nvc = new NameValueCollection(); 
	nvc["enableRssCategories"] = EnableRssCategories.ToString(); 

	return nvc; 
}
C#
public override StatusType SetValues(System.Web.HttpContext context, 
	System.Collections.Specialized.NameValueCollection nvc) 
{ 
	EnableRssCategories = ConvertStringToBool(nvc["enableRssCategories"]); 

	if (EnableRssCategories) 
	{ 
		SetUpRssCategories(); 
	}

	return StatusType.Success; 
}

DataAsNameValueCollection is used internally by Graffiti to store the values for the plugin in its database. SetValues is used to handle any additional setup required both inside Graffiti itself as well as within the event handlers inside the plugin. In this case, I am setting the EnableRssCategories property so it can be used later. ConvertStringToBool is a helper function that was included in the Blog Extensions source code, I’m using it here to perform the same function – to get either true or false for the checkbox value.

C#
private bool ConvertStringToBool(string checkValue) 
{ 
	if (string.IsNullOrEmpty(checkValue)) 
		return false; 
	else if (checkValue == "checked" || checkValue == "on") 
		return true; 
	else 
		return bool.Parse(checkValue); 
}

I am also calling SetUpRssCategories, which is detailed below:

C#
private void SetUpRssCategories() 
{ 
	bool customFieldExists = false; 
	CustomFormSettings cfs = CustomFormSettings.Get(); 
	if (cfs.Fields != null && cfs.Fields.Count > 0) 
	{ 
		foreach (CustomField cf in cfs.Fields) 
		{ 
			if (Util.AreEqualIgnoreCase(categoryFieldName, cf.Name)) 
			{ 
				customFieldExists = true; 
				break; 
			} 
		} 
	}

	if (!customFieldExists) 
	{
		CustomField nfield = new CustomField(); 
		nfield.Name = categoryFieldName; 
		nfield.Description = "Custom Categories you want to included in 
			your RSS feed for your blog post. Enter each on a new line 
			and each item will be entered in a separate <category> 
			element. If you want to set a domain on the category element,
			add a dollar sign ($) after the category and then enter 
			the domain value."; 
		nfield.Enabled = true; 
		 nfield.Id = Guid.NewGuid(); 
		 nfield.FieldType = FieldType.TextArea; 

		cfs.Name = "-1"; 
		cfs.Add(nfield); 
		cfs.Save(); 
	} 
}

SetUpRssCategories sets up the custom field in the Post Editor if it isn’t already there. The top portion of the code is taken directly from the Blog Extensions example, and it loops through the custom fields found in Graffiti to try and find the field with a name that matches what I defined as my custom field name. If it doesn’t find the field, then the bottom portion of the code creates the field and saves it so it can be used in the Post Editor. Note that the field is NOT deleted if the option becomes disabled; this is because you shouldn’t delete data that may have been entered into that field in the past.

With this code, when the checkbox on the edit screen is checked, and the options are saved, the new custom field is added and made available. You must also enable the plugin itself to get this to happen. It should look like this on the Plugins screen:

RSSPluginEnabled

One thing I forgot to mention is how to set up the content of this box. There are two simple properties to override, which provide the title and description of the plugin:

C#
public override string Name 
{ 
	get { return "RSS Extensions Plugin"; } 
}

public override string Description 
{ 
	get { return "Extends Graffiti CMS with advanced RSS Feed options"; } 
}

Once all of these steps are completed, you should be able to use the plugin on any posts. To see it working, you go to any post, and choose the Custom Fields tab. It should look something like this:

RSSPluginCustomFields

The Display Title and GeoRSS Location are associated with other plugins, but the important thing here is the Custom RSS Categories field. Here, categories can be entered for the post. Each category goes on a new line, and can optionally have a domain added to it, per the spec for RSS.

The last part of the code is actually handling the event and making the changes to the RSS feed. Here’s the code to accomplish this:

C#
void ga_RssItem(System.Xml.XmlTextWriter writer, PostEventArgs e) 
{ 
	if (EnableRssCategories) 
	{ 
		string categories = e.Post.Custom(categoryFieldName); 
		if (categories == null || categories.Length == 0) 
		{ 
			return; 
		} 
		string[] categoryItems = categories.Split
		(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

		foreach (string category in categoryItems) 
		{ 
			string[] categoryParts = category.Split
				(new string[] { "$" }, 
				StringSplitOptions.RemoveEmptyEntries); 
			writer.WriteStartElement("category"); 
			if (categoryParts.Length == 2) 
			{ 
				writer.WriteAttributeString("domain", 
						categoryParts[1]); 
			} 
			writer.WriteString(categoryParts[0]); 
			writer.WriteEndElement(); 
		} 
	} 
}

The code simply retrieves the custom data from the post object and then uses the XmlTextWriter that is passed in from the event to add new elements to the current item element. Once this is in place, the RSS feed gets updated to include the <category> in the <item>, like this:

XML
<item>
	<title>Programming for Date Ranges</title> 
	<link>http://www.nexustechnologiesllc.com/blog/programming-for-date-ranges/ 
	</link> 
	<pubDate>Tue, 14 Jul 2009 22:04:00 GMT</pubDate> 
	<guid isPermaLink="true">
	http://www.nexustechnologiesllc.com/blog/programming-for-date-ranges/ </guid> 
	<category>CodeProject</category> 
	<description> 
		Content of post 
	</description> 
	<author>Charles Boyung</author> 
</item>

The added code is the line directly above the <description> element in this sample.

The source code and binary for this project are available at CodePlex: http://graffitirssextension.codeplex.com/.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Architect Nexus Technologies, LLC
United States United States
I have been working in the field of software development since 1999. With a degree in Computer Engineering from the Milwaukee School of Engineering, I try to provide a strong results-oriented approach to software development. I have worked with a variety of industries, including healthcare, magazine publishing and retail. After having worked for corporations of varying sizes for nearly ten years while also providing custom software solutions to individuals and small companies, I left the corporate world to provide expert, high-quality software solutions to a broader range of companies full-time. I am also a Certified Usability Analyst with Human Factors International, committed to providing the best possible experience to the users of your website or application.

Comments and Discussions

 
-- There are no messages in this forum --