AJAX News Rotator in Sitefinity Part 1

I’ve recently been developing a control to display recent news in a rotating fashion. Originally, I was using the RadRotator control from Telerik. This is a powerful control that takes a lot of the guesswork out of displaying and rotating news. However, I decided to build my own for two reasons.

First, I didn’t like the way the rotator control loads ALL of the news items before running. The RadRotator works by preloading all of the elements, placing each item into its own div element, then cycling through each, toggling visibility of each one at a time. This solution works for a few elements, but if you’re cyclying through a half dozen or more items, each with an image, it can be an expensive procedure, especially since the control has to finish loading in order for the page to run. This leaves users viewing half a screen while the content loads.

The other reason I decided to build my own was just out of sheer curiousity and a desire to learn, especially since this is a perfect candidate to get my feet wet in the Ajax world that I’ve been dying to dive into.

Anyway, enough back-story, let’s get to the code! The first thing I had to do was retrieve the news items that will be displayed. I’ll be storing the Guids for each news item in the ViewState for quick, on-demand retrieval. The Guids go in a Generic List, which will be accessed by a CurrentItem index, which checks to make sure that this value is always within the valid range of elements.

protected List newsIDs  
{  
    get {  
        if (ViewState["NewsIDs"] == null) ViewState["NewsIDs"] = new List();  
        return (List)ViewState["NewsIDs"];  
    }  
    set { ViewState["NewsIDs"] = value; }  
}  
 
protected int CurrentItem  
{  
    get {  
        if (ViewState["CurrentItem"] == null) ViewState["CurrentItem"] = 0;  
        return (int)ViewState["CurrentItem"];  
    }  
    set {  
        if (value > newsIDs.Count - 1)  
            ViewState["CurrentItem"] = 0;  
        else if (value < 0)  
            ViewState["CurrentItem"] = newsIDs.Count - 1;  
        else ViewState["CurrentItem"] = value;  
    }  
} 

Since I’m using Sitefinity (my content management system of choice!), I’ll need to use the NewsManager to retrieve these items. Because of the nature of the News API, it is necessary for me to retrieve the full list of news items on Page_Load. Ideally, I would only be grabbing the Guid for each item, but since the only way to retrieve them is to retrieve the full items, I’ll have to do that. Fortunately, this is only in the code behind, and isn’t rendering to the page so the overhead should be minimal, especially since it’s only done on the initial Page_Load

protected void Page_Load(object sender, EventArgs e)  
{  
    if (this.manager == null) this.manager = new NewsManager("News");  
      
    if (!IsPostBack)  
    {  
        BindData();  
    }  
}  
 
private void BindData()  
{  
    IMetaSearchInfo[] filters = new IMetaSearchInfo[3];  
    filters[0] = new MetaSearchInfo(MetaValueTypes.DateTime, "Expiration_Date", DateTime.Now, SearchCondition.GreaterOrEqual);  
    filters[1] = new MetaSearchInfo(MetaValueTypes.ShortText, "Category", "Top Story");  
    filters[2] = new MetaSearchInfo(MetaValueTypes.DateTime, "Publication_Date", DateTime.Now, SearchCondition.LessOrEqual);  
 
    IList newsList;  
    newsList = manager.Content.GetContent("Publication_Date DESC", filters);  
 
    newsNavRepeater.DataSource = newsList;  
    newsNavRepeater.DataBind();  
 
    foreach (IContent content in newsList)  
        newsIDs.Add(content.ID);  
 
    // start with the first news Item UpdateNews((IContent)newsList[0]);  
} 

The newsNavRepeater is simply a repeater of LinkButtons that will tie to the individual news item ids.

<div class="sf_newsRotator"> <telerik:radajaxpanel id="NewsAjaxPanel" runat="server" width="550" height="185" loadingpanelid="NewsLoadingPanel"> <div id="newsFrame" class="newsFrame"> <h3><asp:HyperLink ID="lnkTitle" runat="server" /></h3> <asp:HyperLink ID="lnkFullStory" runat="server" CssClass="fullstory" Text="Full Story &raquo;" /> <div class="sf_newsImage"> <asp:HyperLink ID="lnkThumbnail" runat="server" /> </div> <p><strong style="font-size: .9em;">Posted  
                <asp:Label ID="PublicationLabel" runat="server" /></strong><br /> <asp:Literal ID="SummaryLabel" runat="server" /> </p> </div> <div class="newsNav"> <asp:Repeater ID="newsNavRepeater" runat="server" OnItemDataBound="newsNavRepeater_ItemDataBound" OnItemCommand="newsNavRepeater_ItemCommand"> <ItemTemplate> <asp:LinkButton ID="lnkItem" runat="server" /> </ItemTemplate> </asp:Repeater> </div> </telerik:radajaxpanel> <a class="allnews" href="/news/default.aspx" title="View All News from the City of McAllen"> All News</a> <a href="/news.rss" class="rss" target="_blank" title="Subscribe to the City of McAllen Latest News RSS Feed"> <img src="/images/icons/icon-rss.gif" alt="RSS Feed" style="vertical-align: middle;" /></a></div><telerik:radajaxloadingpanel id="NewsLoadingPanel" runat="server" height="190px" width="550px" transparency="50" enableviewstate="false"> <div style="background: #ccc; height: 100%"> <img style="margin-top: 75px; z-index: 1" alt="Loading..." src='<%= RadAjaxLoadingPanel.GetWebResourceUrl(Page, "Telerik.Web.UI.Skins.Default.Ajax.loading.gif") %>' style="border: 0px;" /> </div> </telerik:radajaxloadingpanel>

The buttons are populated using the OnItemBound event to fill each link with the appropriate post information from the bound IContent DataItem.

protected void newsNavRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    // get link LinkButton lnk = e.Item.FindControl("lnkItem") as LinkButton;

    // get dataitem IContent newsItem = e.Item.DataItem as IContent;
    int itemIndex = e.Item.ItemIndex + 1;
    lnk.Text = itemIndex.ToString();
    lnk.CommandArgument = (itemIndex - 1).ToString();
    lnk.ToolTip = newsItem.GetMetaData("Title").ToString();
} 

Once the navigation is bound, we need to bind the first news item, which is the first item in the newsList collection above (newsList[0]). Note how this item has to be cast as IContent and passed into the UpdateNews method.

private void UpdateNews(IContent newsItem)  
{  
    // make sure item still exists if (newsItem == null)  
    {  
        // if not, reset data! BindData();  
        return;  
    }  
 
    // bind current item data string newsTitle = newsItem.GetMetaData("Title").ToString();  
    lnkTitle.Text = newsTitle;  
    lnkTitle.NavigateUrl = string.Format("/news/default{0}.aspx", newsItem.Url.ToLower());  
    lnkThumbnail.ImageUrl = newsItem.GetMetaData("Thumbnail").ToString();  
    lnkThumbnail.ToolTip = newsTitle;  
    lnkThumbnail.NavigateUrl = lnkTitle.NavigateUrl;  
    lnkThumbnail.Text = newsTitle;  
    SummaryLabel.Text = newsItem.GetMetaData("Summary").ToString();  
    PublicationLabel.Text = ((DateTime)newsItem.GetMetaData("Publication_Date")).ToString("dddd MMMM dd, yyyy");  
 
    LinkButton selectedLink = newsNavRepeater.Controls[CurrentItem].FindControl("lnkItem") as LinkButton;  
    selectedLink.CssClass = "selected";  
} 

Notice also that this method ensures that a valid item was found, if not (such as a newsitem deleted or new items posted) it will reset, binding all new data and start all over again. Also, at the bottom, we since the Controls array of the newsNavRepeater has as many elements as the newsList, we can grab use that index to select the associated LinkButton and set its CSS class so that we can highlight the selected item!

All that is left to do is handle the OnItemCommand event of the newsNavRepeater (since these buttons are what trigger a different item). Remember that the argument for each LinkButton set in BindData is the index of that news item’s Guid in the newsList Collection. So all we have to do is retrieve that one news item (using the News API) and run the UpdateNews method again! Notice how before we update the CurrentItem with the new value, we use it to “unselect” the previous news item.

protected void newsNavRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)  
{  
    // unselect css before changing LinkButton selectedLink = newsNavRepeater.Controls[CurrentItem].FindControl("lnkItem") as LinkButton;  
    selectedLink.CssClass = "";  
 
    CurrentItem = Int32.Parse(e.CommandArgument.ToString());  
    IContent newsItem = manager.Content.GetContent(newsIDs[CurrentItem]);  
    UpdateNews(newsItem);  
} 

And that’s all there is to it! Wrap all of this inside of a RadAjaxPanel and you’ve got yourself an interactive news roll! There’s still a few things to add, namely a Timer control to automatically cycle the news, but I will cover that my next entry. I hope this has been helpful, and as always your comments are welcome and appreciated!

Serving Files from another domain

I’ve recently needed to develop a method to allow users to access files that are on a restricted domain. This domain is on an internal server that has no external access. However, it serves documents and files to a webserver that is accessible externally. So the webserver has access to the files, meaning they can upload files to the webserver, which will then save them to the internal server… But the reverse is not true. So we needed a way to “copy” files from the internal server to the end user.

My solution was to use an ASHX handler that will process the users request and retrieve the requested document, streaming it to the user through the response output stream. this is relatively new territory for me, but nothing ventured nothing gained!

The file is located by retrieving parameters passed through the querystring for the date, folder, and filename. This is mapped to a location on the webserver. I begin by validating the input. This probably isn’t the best method for validating input but it should do the job until I have more time to optimize it. Please feel free to suggest anything I’ve overlooked!

	// retrieve qstring fields, ensure all are present
	string date = context.Request.QueryString["date"];
	string folder = context.Request.QueryString["key"];
	string file = context.Request.QueryString["file"];
	if (string.IsNullOrEmpty(date) || string.IsNullOrEmpty(folder) || string.IsNullOrEmpty(file))
	{
		context.Response.Write("File not found");
		context.Response.End();
	}
		
	// prevent directory attacks
	if (date.Contains("../") || folder.Contains("../") || file.Contains("../"))
	{
		context.Response.Write("File not found");
		context.Response.End();
	}

Next I built a webrequest to retrieve the file by formatting a url string and retrieveing the response (which is the pdf file itself);

        // build request stream
	string url = string.Format("http://INTERNALIP/{0}/{1}/{2}", date, folder, file);
	HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
	HttpWebResponse resp = null;
	try
	{
		 resp = (HttpWebResponse)req.GetResponse();
	}
	catch (Exception ex)
	{
		context.Response.Write("File not found");
		context.Response.End();
	}

	Stream rStream = resp.GetResponseStream();

Finally, I initialize the response, filling it with the binary data from the pdf file, and writing to the stream as a pdf file attachment.

	// initialize response
	context.Response.Clear();
	context.Response.ContentType = "application/pdf";
	context.Response.AddHeader("Content-disposition", string.Format("attachment; filename={0}", file));
		
	// create buffer to input data
	int buffersize = 1024 * 16;
	byte[] buffer = new byte[buffersize];
	int count = rStream.Read(buffer, 0, buffersize);

	while (count > 0)
	{
		// write content to response
		context.Response.OutputStream.Write(buffer, 0, count);
		count = rStream.Read(buffer, 0, buffersize);
	}

	context.Response.End();

Now all that is necessary is to build the url to the handler with the appropriate fields in the querystring, like http://externalsite.com/pdf.ashx?date=20080828&folder=stuff&file=thefile.pdf and sure enough the handler does all the work! Not too shabby for my first real ashx application!

As always your comments are welcome and appreciated

ObjectDataSource QueryStringParameter Input Validation

Procrastination will only get you so far before you have to finally just put it off and get your ass to work. Today I finally get back into the swing of things with my first post since my sites (still incomplete) migration to SiteFinity Content Management System. I know there is still much to do with existing content, like fixing broken links and all that nonsense, but I’m taking a positive, constructive approach and getting things started with a revival of my now merged Software Blog. Today we talk about validating input parameters for the asp.net objectdatasource control.

As you likely don’t already know, I’ve just recently launched the new City of McAllen Website (a post on the impact this opportunity has had on my life is coming soon), and in one section in particular, I needed to make use of the QueryString to input parameters to an ObjectDataSource for selecting specific items from our Database. Straightforward enough yes?

The Problem

The problem is that I did not have any way to validate input to ensure that the parameters were of the correct type. Of course I made use of stored procedures and strongly-typed parameters to ensure that invalid input isn’t processed. However, these only prevent bad code from executing, and don’t actually do anything to prevent an invalid argument exception if users make an error (or worse, attempt to inject malicious code, the bastards). As a result, if the QueryString does not resolve automatically to the expected type (in this particular case Int32), an exception is thrown, and the user is shown a generic error message. Not very user friendly for a city that prides itself on its high levels of satisfaction from its citizens!

The simplest solution to implement seemed to be to check the querystring on Page_Load, which for all practical purposes should have worked just fine. This particular page, however, has multiple uses, and does not ALWAYS require a querystring. So I would have to account for this situation when checking, first verifying that I’m the correct mode before validating the querystring. Not only does this add overhead to the implementation cost, but it isn’t practical, since the querystring isn’t coupled to the Page_Load, it is coupled to the input parameters of the ObjectDataSource, so it is there that the validation should occur.

The Solution

Naturally, this lead me to the ObjectDataSource’s OnSelecting event, which occurs right before the datasource Select Command is executed, a natural place to perform validation! Using the now famous Int32.TryParse, it’s a no-brainer to ensure that the requested parameter is an integer:

int bID;
        if (!Int32.TryParse((string)e.InputParameters[“ID”], out bID))
            e.InputParameters[0] = bID;

As you can see, the input parameter is checked for validity, and modified if it doesn’t resolve to an integer. Additionally, since on failure, Int32.TryParse resolves the output integer parameter to a zero, I can use this as a dummy value to ensure that the FormView to which this ObjectDataSource is attached renders the EmptyViewTemplate, informing the user that the requested item could not be found. Much more informative and user-friendly than some random “OOPS” message!

The Caveat

Unfortunately, this didn’t work! At least, not yet. When I tested the page, passing an invalid, non-integer in the querystring, the page continued to throw an exception, showing that the input string was not valid. I knew that I was handling the validation in the right spot, and I knew that it should resolve all input to a valid integer, 0 in the case of an error… However, running the site in Debug mode, I discovered that the OnSelecting event wasn’t even firing! Something was definitely wrong here… of to The Google I went…

Interestintly enough, the first link I found was to a great article by Erwyn Van der Meer on this very subject, reaching the exact same conclusion as I did. Confident that I was at least on the right track, I reviewed his article to find where I might have gone wrong…

I didn’t find the explicit reason for why things weren’t working, but something Erwin wrote caught my attention:

…you can even specify that a parameter should be of type Int32, but the page crashes nonetheless.

This immediately reminded me that I had declared the QueryStringParameter to explicitly be Int32. This means that the ObjectDataSource itself automatically expects and assumes that the input parameter will be of the correct, int32 parsable type, and will attempt to resolve the parameter itself before it executes its Select Command! This is not what I want; I want my code to validate the input and cast the parameter to an appropriate value. So I needed simply to go into the page’s source code and modify the parameter declarations from this:

<asp:QueryStringParameter Name=”ID” QueryStringField=”ID” Type=”Int32″ />

to this

<asp:QueryStringParameter Name=”ID” QueryStringField=”ID” />

I made the changes, saved, and success! The page successfuly executed the OnSelecting code, invalidating my bad input and forcing the value to zero, showing me (and more importantly, the general public) a much more helpful message.

The Lesson

Remember when handling the OnSelecting event for parameter validation to ensure that the input parameters are not explicitly cast in the source view, but instead are handled by your own validation code. Til next time, happy coding!