Group Mirror Feeds

One of the projects I have been involved in recently had a requirement to allow a group blog to have content obtained from an external feed. This as it turns out was a simple task of Community Server customization and I thought I'd write a short article on the thought process behind achieving the task as well as sharing the code.


Initial Analysis

The first stage in producing this customization was to ascertain how much of the existing CS functionality I could easily re-use. I started at the database and had a look at the cs_RollerBlogFeeds table. From the table design I could see that apart from a SectionID and SettingsID there were no other linkages to other tables in CS. Because the feed was tied to a SectionID I could immediately make a guess that if it worked for a standard blog section then it should work for a group blog.

My next stage of analysis was to perform an experiment. The blog feeds are created by a CS job, I needed to know if this job would work correctly when using a group blog.

To prove that the CS job would work I created a group with a blog and looked up the group blog section ID in the database. I then created an entry in the cs_RollerBlogFeeds using the standard Blog admin page and edited the database entry to update the sectionID to be that of my group blog. After the CS job had completed I checked the group blog to find that the mirrored blog had been created in my group blog.

Now that I had confirmed that the blog feed architecture would support what I was after I needed to actually start writing some code.

Development

From the information I had gathered during analysis I figured that development would need the following:

  1. A chameleon control that lists all of the group blog mirror feeds

  2. A chameleon control that displays a singular mirror feed

  3. A chameleon form that allows a user to add and edit a mirror feed

  4. Site urls modifications, to support new mirror feed form

  5. Edit the current editgroup.aspx file to include new functionality

1. Chameleon List

The first control to create was a list control that would bind to a list of RollerBlogFeed objects. This class is a simple control that is inherited from PreTemplatedListBase, the standard chameleon list control. I overrode the DataBind method to get the current group blog and then using the static method in RollerBlogFeeds obtain a list of feeds for this section.

        public override object DataSource
{
get
{
if (!HubConfiguration.Instance().IsSubApplicationDisabled(ApplicationType.Weblog))
{
if (_feeds == null)
{
Hub currentHub = HubControlUtility.Instance().GetCurrentHub(this);

PermissionsCheck(currentHub);

Weblog subApplicationContainer = currentHub.GetSubApplicationContainer
(ApplicationType.Weblog, false) as Weblog;

if (subApplicationContainer != null)
{
_feeds = new List<RollerBlogFeed>((RollerBlogFeed[])
RollerBlogFeeds.GetEnabledFeedsBySectionID
(subApplicationContainer.SectionID).ToArray(typeof(RollerBlogFeed)));
}
}

}
return _feeds;


Notice there is a permissions check, I have assumed that only system administrators and group owners are able to see the list of feeds.

2. Chameleon Data

The list control is only a repeater and I needed to create a data aware control to display the feed information. Additionally I realized that the group edit page would not support post backs and that the list, data and any actions that occurred would need to happen using AJAX.

The created control inherits from ObjectDataBase and is a straightforward implementation that data binds to its parents' RollerBlogFeed. It also includes a property to handle AJAX delete requests. To support the AJAX requests I added an additional property called groupWeblogMirrorFeedAjaxHandlerId which is used to identify the control that will handle AJAX requests. I then extended GetPropertyValue in the control to return a formated AJAX request for the delete action.

   protected override object GetPropertyValue(string property)
{
RollerBlogFeed thisFeed = DataSource as RollerBlogFeed;

switch (property.ToLower())
{
case "ajaxdelete":
if (thisFeed != null)
{
if (string.IsNullOrEmpty(HubWeblogMirrorFeedAjaxHandlerId))
throw new ApplicationException("TO use the Ajax Functionlalty you need to specify HubWeblogMirrorFeedDataID");

Control ctrl = CSControlUtility.Instance().FindControl(Page, HubWeblogMirrorFeedAjaxHandlerId);
if (ctrl != null)
//Return a javascript fragment that calls back into this class and deletes the selected item
return string.Format("HubWeblogMirrorFeedData.DeleteFeed('{0}', {1});", ctrl.ClientID, thisFeed.UrlId);
}
break;
}

return base.GetPropertyValue(property);
}


I then created a AJAX handler class to handle the requests.

3. Chameleon Form

When a user edits a group, they are able to enable and disable the blog. Within the blog administration section the group owner or system administrator should be able to manage the list of external blog feeds. To do this I needed to create a form that supported entering all of the data to manage a blog feed. Fortunately most of this information had already been done in the blog system administration pages and using the SDK I was able to implement the same code inside a chameleon form control. The control that I implemented used the WrappedFormBase base class and used all of the fields that had been identified from the blog system administration control panel screen.

4. Site Urls

In order to add and edit the feed from the edit group page, I needed to update the site urls to include a new url that matches the edit feed page and re-writes it to the theme location. To do this I simple created a siteurls_override.config file and added the required url location.

4. Web Page Changes

Finally I edited the edithub.aspx page and added my new form and created a new editform.aspx page. In order to allow the feed list to update without submitting the group page I used a delayed content control and ajax to manage the list.

<script language="javascript" type="text/javascript" >
function reloadFeedList(res)
{
//Tell this delayed content to reload
<%= CSControlUtility.Instance().FindControl( EditHubForm , "DisplayHubBlogFeeds").ClientID %>.Reload();
}
</script>
<%-- This must go outside the delayed content so the ajax stuff registers correctly on the page--%>
<FRW:HubWeblogMirrorFeedAjaxHandler runat="server" id="HandlerAjaxHubFeed" ></FRW:HubWeblogMirrorFeedAjaxHandler>
<CSControl:DelayedContent ID="DisplayHubBlogFeeds" runat="server" LoadImmediately="true">
<ContentTemplate>
<CSControl:ResourceControl runat="server" tag="h4" ResourceFile="FourRoads.Hubs.BlogMirror.xml" ResourceName="Hub_Feeds_Title">
</CSControl:ResourceControl>
<input onclick="Telligent_Modal.Open( '<%# CommunityServer.Components.SiteUrls.Instance().UrlData.FormatUrl("HubFeedsForm" ,CSControlUtility.Instance().GetCurrentSection(this).ApplicationKey,"-1") %>', 600, 300, reloadFeedList);" type="button" value="Add" />
<FRW:HubWeblogMirrorFeedList runat="server" ShowHeaderFooterOnNone="true" >
<HeaderTemplate>

 

Summary

The implementation I have created is a very simple way of adding external feeds to group blog. I have taken some shortcuts and there is more business logic in the controls than I would have liked.