LibrarySites.Banner

Filtered RSS feed using the Sitecore rules engine

In a prior life, I had a need to create a custom RSS feed in Sitecore that could dynamically exclude certain items from the feed based on specific conditions.

My first thought was to simply use a Sitecore query in the feed item's "Items" field, as the field allows you to either specify a root item whose descendants will be included in the feed or use Sitecore query to specify which items will be included in the feed. This seemed like a valid approach until I started writing the query and it quickly turned into a garbled mess - I'm not a fan of complex xpath queries. On top of that, I could never expect an average content author to understand the Sitecore query syntax or maintain a complex query.

I thought "it would be nice if an average content author could use an intuitive, somewhat familiar interface to specify the logic/conditions that should be used to exclude items from the RSS feed". Enter the ever powerful but woefully under-used Sitecore rules engine and the Rules field type. By creating a custom RSS feed template, adding a Rules field, and extending the standard Sitecore RSS feed class, this turned out to be a pretty easy task.

Create a new RSS feed template

Create a new template that inherits the "/sitecore/templates/system/feeds/RSS Feed" template. It's important that this inheritance occurs otherwise the standard Sitecore RSS feed rendering will not work and you would need to roll your own feed manager (let's try to avoid that).

With your new template created, add a field of type "Rules". I named the field "Exclude Item Rules" but feel free to rename to suit your needs.

Extend the standard Sitecore RSS public feed class

The Sitecore.Syndication.PublicFeed class has a number of methods available for overriding to assist your efforts in creating custom RSS feeds. Fortunately, for the scope of this post, we only need to override the GetSourceItems method.

The gist of the code below is to first retrieve all the items for the given RSS feed, then retrieve the exclusion rules defined in the RSS feed item, apply those rules to the RSS feed items and ignore any items matching the exclusion rules. (Hat tip to Reflector for the assist in reflecting through the Sitecore rules engine code to determine how to retrieve, parse and apply rules)

using System.Collections.Generic;
using System.Linq;
using Sitecore.Data.Items;
using Sitecore.Rules;

namespace Sitecore.SharedSource.Syndication
{
	public class RulesFilterFeed : Sitecore.Syndication.PublicFeed
	{
		public override IEnumerable<Item> GetSourceItems()
		{
			//Get the unfiltered items from the RSS feed
			IEnumerable<Item> unfilteredItems = base.GetSourceItems();

			//Get the rules to process from the current feed item.
			//If no rules exist, return the unfiltered items.
			var excludeRules = GetExclusionRules<RuleContext>();
			if (excludeRules == null)
				return unfilteredItems;

			//Filter the items based on the defined exclusion rules.
			//The expression below basically loops through each unfiltered item and evaluates each defined exclusion rule against the item.
			//If the unfiltered item matches one of the exclusion rules, don't add it to the filtered items list.
			IEnumerable<Item> filteredItems = (from item in unfilteredItems
											   let ruleContext = new RuleContext { Item = item }
											   from rule in excludeRules.Rules
											   where !rule.Evaluate(ruleContext)
											   select item);
			return filteredItems;
		}

		protected virtual RuleList<T> GetExclusionRules<T>() where T : RuleContext
		{
			//Retrieve the rules field defining the exclusion rules.
			//If the field doesn't exist, exit the method.
			var excludeField = FeedItem.Fields["Exclude Item Rules"];
			if (excludeField == null)
				return null;

			//Use the Sitecore.Rules.RuleFactory class to get the rules from the field
			return RuleFactory.GetRules<T>(excludeField);
		}
	}
}

Create a RSS Feed

The final step in the process is to create a new RSS feed item based on the RSS feed template you created earlier. Prior to doing so, though, I would recommend editing the RSS feed template Standard Values item - populating the Type field with the name of the extended public RSS feed class you created. Populating this field instructs Sitecore to use your extended class to process and render the RSS feed.

While you do have the option of populating this field at the RSS feed item level, populating this field in the Standard Values item ensures that all RSS feed items based on your new RSS feed template automatically use your extended class.

In your newly created RSS feed item (based on your newly created RSS feed template), you should set the "Items" field as you normally would – indicating the item to use as the root for your RSS feed items. You should also see the "Exclude Item Rules" field (or whatever you ended up naming it). You (or better yet, your content authors) can use this field like any other item rules field to specify rules for excluding items from the RSS feed.

Conclusion

And that's it. Pretty easy right? Just another example of the flexiblity and extensibility Sitecore provides. Enjoy!