Adding Tapped Event Gestures to Any XAML Control with Xamarin Forms

We previously looked at how to create User Controls with Xamarin Forms to display an Image Button for the different activities in our Falafel 2 Go application. Now that we have these reusable buttons, we need to make them tab-enabled so that they can launch the appropriate activity. As we’ll see in today’s post, this is very simple to implement with Xamarin Forms.

Read more at the Falafel Software Blog: Adding Tapped Event Gestures to Any XAML Control with Xamarin Forms

Porting a Windows App Studio Universal App to Android Using Xamarin

Microsoft’s App Studio is a fantastic tool to help you design and generate applications for Windows Phone as well as Universal Apps for both the phone and Windows. The online interface allows you to add content like RSS feeds, Facebook pages, Flicker photos and more with a few clicks, generating a complete Visual Studio solution that can immediately be launched on the phone or desktop.

Today we’ll look at how we can enhance the App Studio solution’s Portable Class Library so that we can use Xamarin to add an Android version of the app.

Read more at the Falafel Software Blog: Porting a Windows App Studio Universal App to Android Using Xamarin

Sitefinity Toolkit Update October 2010

It took longer than I’d hoped to put the project together allowing me to support the Sitefinity Toolkit on all versions of Sitefinity 3.7, on ASP.NET 2.0, 3.5, AND 4.0 all the way up to the recently released SP4. However, after after a few updates to the project and adding some 2.0 and 4.0 compatible libraries, it looks like things are good to go!

However, I’m not confident enough to put them out into production simply because I have not tested them. I have a few sites I’m still running, but none like the half dozen I used to manage before joining Telerik! So instead, I’m turning to you, fellow Sitefinity developers for assistance.

If you would like to help me beta test the new release of the Toolkit (will be version 1.2, and I’ll post a full change log when I release it hopefully early next week), please get in touch with me. Email me at josh at selarom dot net and I’ll give you a link to the full download package which (hopefully!) includes support for all versions of Sitefinity 3.7.

Remember that this is still a personal project, and is in no way guaranteed or officially supported by Telerik or any other person but myself. So please use at your own risk and TEST, TEST, TEST!. Any feedback would be greatly appreciated, and very helpful to getting this thing released out to the masses.

Free For Life

If I haven’t already mentioned this before, because of the recent changes in my career, I’ve decided now to permanently make the Sitefinity Toolkit free for use by anyone without restrictions. While I will not be releasing the source code, I will do what I can to help and continue to add features as long as there is interest! And I certainly won’t turn down a donation for my efforts 🙂

Sharing My Experience

There was an unexpected result from my attempt to put this new update together; setting up the Visual Studio solution to support multiple versions of Sitefinity was an interesting challenge. However, I took a lot of notes along the way, and want to share this experience with other Sitefinity developers so that they too can more easily support multiple versions of Sitefinity from a single code-base.

Although Sitefinity 4.0 is right around the corner, there is still a lot of developers active in the Sitefinity Marketplace. I hope that I can help make things easier by putting together a blog post detailing my experience attempting to support all versions of the latest Sitefinity 3.7. Look out for that within the next week, I hope to have it up soon!

In the meantime, if you have any questions, or want to help test the new Sitefinity Toolkit beta, please get in touch with me, thanks!

Important Sitefinity Toolkit Update

Greetings all, I know it’s been a while since I’ve updated here, but work, life, and everything once again has me by the throat, but not in a bad way, really. But as always, the first things to be sacrified are side projects like this.

One thing I did manage to do was finally install the Sitefinity Toolkit on my own website (always the last to be updated right?) meaning if you’ve been coming here looking for stuff and get broken links and 404 pages, you’ll finally be able to see a helpful 404 page with search results that might help you find what you’re looking for. If you still can’t find something or something is broken, be sure to leave a comment on the new Disqus system so I can get back to you!

Sitefinity Toolkit Update

Speaking of the toolkit, I’m now considering the options of making future updates to the toolkit paid releases. I know that I said previously that it would always be free, but with freelance work down, the economy struggling, and a recent rent increase, I just can’t afford to invest the time I have into the project and not use it to at least TRY to offset my ever increasing financial burden.

The decision has not yet been made and yes, I do realize that I’ve said on many occasions that it would always be free, but times, as we all know, are difficult. Things are especially tough for me, living in an area with almost NO opportunity for professional web developers like myself. Moving is unfortunately out of the question, so I have to do what I can to survive…

One thing is for certain: the CURRENT version of the toolkit (version 1.1.0) WILL AWLAYS be FREE and available for download. I will never take it down or charge for it! It’s been downloaded hundreds of times and I hope that it has been helpful to everyone who has used it! But for now, I need to do some serious reflecting on where I want to go and what I want to do with this project, so until then, the next release is on hold.

What do you think? Would you consider paying for new features and updates to the toolkit? What is it worth to you? What kind of support expectations would you have?

Your input is very important to me, and will have a big impact on what I decide to do. If I don’t hear from ANYONE, I’ll use that as evidence that I should just go ahead and invest the time towards developing a paid product.

Thank you to everyone for your feedback, suggestions, and help in developing this project!

Sitefinity: Rss Feeds for Generic Content – Part 2

 

As I mentioned in part one of this series, building an Rss feed for the Generic Content Module (or any module for that matter) requires three classes, RssChannelProvider, RssSettings, and RssView (plus the rss feed settings user control). We already covered the basic RssView, so now in this part we take a more detailed look at the changes required to the other two classes to implement the full Rss functionality.

RssSettings

The bulk of the rss setup is handled using this class, so most of our attention will be here. This is the class that facilitates the “setup” of your feed, including the feed title, description, and content to be syndicated. Since this is the link between the feed settings stored in the database and the settings user control, we use another class ControlContainer inside of the RssSettings class that handles references to all the input controls:

protected class ControlContainer : GenericContainer<GenericContentRssSettings>
{
    public ControlContainer(GenericContentRssSettings owner) : base(owner) { }

    private ListControl providerList;
    public ListControl ProviderList
    {
        get {
            if (providerList == null)
                providerList = base.FindRequiredControl<ListControl>("ProviderList");
            return providerList;
        }
    }

    private IEditableTextControl enclosureMetaField;
    public IEditableTextControl EnclosureMetaField
    {
        get {
            if (enclosureMetaField == null)
                enclosureMetaField = (IEditableTextControl)base.FindControl(typeof(IEditableTextControl), "EnclosureMetaField", true);

            return enclosureMetaField;
        }
    }

    private IEditableTextControl postUrl;
    public IEditableTextControl PostUrl
    {
        get {
            if (this.postUrl == null)
            {
                this.postUrl = (IEditableTextControl)base.FindControl(typeof(IEditableTextControl), "postUrl", true);
            }
            return this.postUrl;
        }
    }

    private IEditableTextControl summaryLength;
    public IEditableTextControl SummaryLength
    {
        get {
            if (this.summaryLength == null)
            {
                this.summaryLength = (IEditableTextControl)base.FindControl(typeof(IEditableTextControl), "summaryLength", true);
            }
            return this.summaryLength;
        }
    }

    private ListControl syndicationType;
    public ListControl SyndicationType
    {
        get {
            if (this.syndicationType == null)
            {
                this.syndicationType = base.FindRequiredControl<ListControl>("syndicationType");
            }
            return this.syndicationType;
        }
    }

    private IEditableTextControl itemsCount;
    public IEditableTextControl ItemsCount
    {
        get {
            if (this.itemsCount == null)
            {
                this.itemsCount = (IEditableTextControl)base.FindControl(typeof(IEditableTextControl), "itemsCount", true);
            }
            return this.itemsCount;
        }
    }

    private ListControl categoryList;
    public ListControl CategoryList
    {

        get {
            if (this.categoryList == null)
            {
                this.categoryList = base.FindRequiredControl<ListControl>("CategoryList");
            }
            return this.categoryList;
        }
    }
}

Note that this control uses the generic ListControl and IEditableTextControl instead of DropDownList and TextBox. This allows you to use more complex versions of these controls (such as Telerik RadControls) without breaking functionality. All this class does is return references to the individual input controls on the Rss Feed Settings User Control I mentioned last time. You might want to include error handling (such as if the control is not found, or if the control is of the wrong type).

Now that we have the link between settings and user input, we need to override the CreateChildControls method. This is the method that instantiates all the input user controls using the ControlContainer class and populates them with existing settings (if any) so that they can be saved to the database on creating (or update). I’ve done my best to comment the sequence of events below.

 

protected override void CreateChildControls()
{
    ctrlContainer = new ControlContainer(this);

    // instatniate the settings user control if (this.controlTemplate == null)
    {
        // make sure it exists if (File.Exists(this.Page.MapPath(this.ControlTemplatePath)))
        {
            this.controlTemplate = this.Page.LoadTemplate(this.ControlTemplatePath);
        }
        else {
            throw new NullReferenceException("Control Template does not exist");
        }
    }
    this.controlTemplate.InstantiateIn(this.ctrlContainer);

    // retrieve list of content providers string[] strArray = null;
    int count = ConfigHelper.Handler.Providers.Count;
    if (count > 1)
    {
        int num2 = 0;
        strArray = new string[count];
        foreach (ProviderSettings element in ConfigHelper.Handler.Providers)
        {
            // we only want the generic content providers if (element.Type.StartsWith("Telerik.Cms.Engine.Data.Providers.DefaultProvider"))
            {
                // make sure we have permission GlobalPermission permission = new GlobalPermission((GlobalPermissions)ContentManager.SecurityRoots[element.Name], CrudRights.View);
                if (permission.CheckDemand())
                {
                    // add to list of providers strArray[num2++] = element.Name;
                }
            }
        }
    }

    // add list of providers to dropdown list for selection ListControl providerList = this.ctrlContainer.ProviderList;
    if (strArray != null)
    {
        foreach (string str in strArray)
        {
            if (!string.IsNullOrEmpty(str))
            {
                ListItem item = new ListItem(str.Replace('_', ' '), str);
                providerList.Items.Add(item);
            }
        }
    }
    this.ctrlContainer.ProviderList.Visible = providerList.Items.Count > 1;
    this.ctrlContainer.ProviderList.AutoPostBack = true;

    // handle changes to selected provider to show its categories this.ctrlContainer.ProviderList.SelectedIndexChanged += new EventHandler(this.ProviderList_SelectedIndexChanged);

    // populate syndication type menu this.ctrlContainer.SyndicationType.Items.Add(new ListItem("Full Content", Enum.GetValues(typeof(SyndicationType)).GetValue(0).ToString()));
    this.ctrlContainer.SyndicationType.Items.Add(new ListItem("Summary", Enum.GetValues(typeof(SyndicationType)).GetValue(1).ToString()));
    this.ctrlContainer.SyndicationType.Items.Add(new ListItem("Title Only", Enum.GetValues(typeof(SyndicationType)).GetValue(2).ToString()));

    // grab settings from db and load into user control if ((this.settings != null) && (this.settings.Count > 5))
    {
        this.ctrlContainer.SyndicationType.SelectedValue = this.settings["SyndicationType"];
        this.ctrlContainer.ProviderList.SelectedValue = this.settings["ProviderName"];
        this.ctrlContainer.PostUrl.Text = this.settings["ItemUrl"];
        this.ctrlContainer.ItemsCount.Text = this.settings["ItemCount"];
        this.ctrlContainer.SummaryLength.Text = this.settings["SummaryLength"];
    }

    // set Provider and category settings["ProviderName"] = ctrlContainer.ProviderList.SelectedValue;
    settings["Category"] = ctrlContainer.CategoryList.SelectedValue;

    // load category menu and select current option PopulateCategories();
    if (this.ctrlContainer.CategoryList.Items.FindByValue(settings["Category"]) != null)
        this.ctrlContainer.CategoryList.SelectedValue = settings["Category"];

    // render control to the page this.Controls.Add(this.ctrlContainer);
}

You may have noticed that I added an event handler to the SelectedIndexChanged event in the ProviderList menu. This calls a method to retrieve the list of categories for that provider so that we can show its categories when we change to it. This method calls the same PopulateCategories() method from the initial setup.

protected void ProviderList_SelectedIndexChanged(object sender, EventArgs e)
{
    ListControl lst = sender as ListControl;
    settings["ProviderName"] = lst.SelectedValue;
    PopulateCategories();
}

private void PopulateCategories()
{
    this.ctrlContainer.CategoryList.Items.Clear();
    string provider = this.settings["ProviderName"];
    ContentManager mgr = new ContentManager(provider);
    IList cats = mgr.GetCategories();
    foreach (Telerik.Cms.Engine.Data.Category cat in cats)
    {
        this.ctrlContainer.CategoryList.Items.Add(cat.CategoryName);
    }
}

This allows us to syndicate only specified categories of content from the specific provider. I did this because I only want to publish a feed of my Dark Synthesis Mixes in my podcast, as opposed to all of my music. You will see how this works in the next section, however this can easily be modified to ignore the category meta field and show all content from the provider.

The last thing we need to do for this class is make sure that we have a method to save the settings to the database. This is done by overriding the SaveSettings() method.

public IDictionary<string, string> SaveSettings()
{
    settings = new Dictionary<string, string>();
    settings["ProviderName"] = this.ctrlContainer.ProviderList.SelectedValue;
    settings["ItemUrl"] = this.ctrlContainer.PostUrl.Text;
    settings["ItemCount"] = this.ctrlContainer.ItemsCount.Text;
    settings["Category"] = this.ctrlContainer.CategoryList.SelectedValue;
    settings["SyndicationType"] = this.ctrlContainer.SyndicationType.SelectedValue;
    settings["SummaryLength"] = this.ctrlContainer.SummaryLength.Text;
    return settings;
}

That’s pretty much it for this class. As you can see, editing settings for the feed is pretty straight forward. Just make sure you add the control to the Feed Settings User Control, as well as a proeprty to the ControlContainer class to reference it. Then make sure that your settings are saved to the database in the SaveSettings and loaded in the CreateChildControls method. Now lets move on to the class that actually creates the feed itself!

RssChannelProvider

This is the class that is responsible for building the feed itself. Luckily, the mechanics of actually writing the xml are abstracted away by an RssItem class, so all we have to do is create these items and pass them along to the class and it will handle the rest! The first thing you want to do is expand the Initialize method to extract the relevant settings from the internal settings property (this is the same dictionary of elements from the previous RssSettings class) and map them to local private variables for later use.

private string EnclosureMetaField;
private string Category;
private int ItemCount;
public override void Initialize(IDictionary<string, string> settings)
{
    base.Initialize(settings);
    Category = settings["Category"];
    ItemCount = Int32.Parse(settings["ItemCount"]);
}

Next we need to override the CreateDataSource method to retrieve the appropriate list of items using the settings  values we just retrieved.

protected override IList CreateDataSource()
{
    ContentManager mgr = new ContentManager(this.ProviderName);
    if (string.IsNullOrEmpty(Category))
        return mgr.GetContent("Publication_Date DESC");
    else {
        List<IMetaSearchInfo> filter = new List<IMetaSearchInfo>();
        filter.Add(new MetaSearchInfo(MetaValueTypes.ShortText, "Category", Category));
        return mgr.GetContent(0, ItemCount, "Publication_Date DESC", filter.ToArray());
    }
}

As you can see, this is just a matter of initializing the content manager with the correct ProviderName. the base Initialize already populates the internal ProviderName property with the correct provider name! Notice how we make use of the Category property to filter the results and only show the requested category. It is here that you can make changes to the list of items that are retrieved based on whatever logic you might require to suit the needs of your feed.

Now the DataSource creates an IList of IContent, which is the basic GenericContent class. We need to map this into the RssItem class so that it can be properly rendered. So the final method we need to override is GetRssItems, which creates the individual items for the feed.

public override IList<RssItem> GetRssItems(System.Collections.Specialized.NameValueCollection query)
{
    IList<RssItem> rssItems = base.GetRssItems(query);
    if (rssItems.Count == 0)
    {
        RssItem item = new RssItem();
        item.Title = "Empty Blog";
        rssItems.Add(item);
    }
    return rssItems;
}

As you can probably tell, most of the work is handled by the base method. However, if the feed contains no items, you are going to get an exception stating that the feed cannot be closed without writing an entry. Basically what this means is that you have an empty blog. I handle this exception by checking for an empty list and if that is the case, creating a blank dummy item to show information and prevent the error. You could also use this method to do any last minute changes to your feed items (such as changing the publish time, title, or anything else you might need to modify at the last moment before publication).

And that’s pretty much it! With just these two parts you have the basics for creating your own feeds from GenericContent, or any custom module in Sitefinity, once again proving the versatility and extensibility of this great CMS package (not to mention how much FUN it is!). We have just one more part to go, which is modifying the feed to handle enclosures (read: podcasts!). Stay tuned!

Sitefinity: Rss Feeds for Generic Content – Part 1: Setting Up

After finally getting my music back online earlier this week, the next logical step was to get my Dark Synthesis Podcast back online. Unfortunately, Sitefinity (the CMS this site uses) does not have built in support for podcasts. In fact, the Generic Content Module (which I am using to publish my music, and eventually hopefully my videos) doesn’t even have support for general rss feeds! Fortunately to me, however, this wasn’t a setback; it was yet another opportunity to dive deeper into Sitefinity and learn to do something new!

In a recent forum on the Sitefinity forum, I discussed how to implement rss feeds for Sitefinity intra-site modules. This actually works great, and if any one is interested I’d be glad to make a more detailed blog entry about how this is done… But this site doesn’t use custom modules (not yet anyway); music and videos are published using the Generic Content module. The only Rss providers built into Sitefinity are for Pages, Blogs, and Forums.

Fortunately, in what is turning out to be a life-changing moment, I discovered Reflector, possibly one of the greatest tools a .NET developer can have! With it, I was able to take a peek at the existing rss providers (I used the Blog Rss Provider as an example) and use them as a guide to build my very own implementation for the Generic Content Module!

As a result I’m proud to begin the first of a three-part series on how to create an Rss Feed from the Generic Content Module. Part 1 introduces the various classes and controls that need to be created. Part 2 will show you the various methods that need to be overridden within each class. Finally Part 3 will show you how to turn your Rss Feed into a Podcast!

Okay enough back story… let’s get to the code!

Required Classes

There are three classes that will need to be created: a ChannelProvider, RssSettings, and RssView. I’ll introduce each class briefly here, then go into more detail of each one later on in this post.

The real meat of the Rss feed comes from the ChannelProvider and RssSettings classes; rhe RssView class is only used to show a read-only view of the Rss settings in the Services page, so a full implementation isn’t required. Since I didn’t really use this, but since it is required, I’ll just display the minimum code which I used to get it functioning:

using System;
using System.Collections.Generic;
using System.Web;

public class GenericContentRssViewControl : System.Web.UI.WebControls.CompositeControl, Telerik.Framework.Rss.IRssViewControl{
    #region IRssViewControl Members

    IDictionary<string, string> _settings;
    public void InitializeSettings(IDictionary<string, string> settings)
    {
        _settings = settings;
    }  

    #endregion}

Now you need to create a class that inherits from the RssChannelProvider. The basic overrides are below, but we’ll be jumping back and forth as we create the additional classes.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Net;
using Telerik.Rss;
using Telerik.Cms.Engine;

public class GenericContentRssProvider : RssChannelProvider{
    public override void Initialize(IDictionary<string, string> settings)
    {
        base.Initialize(settings);
    }

    public override string Name
    {
        get { return "Generic Content"; }
    }

    public override string Description
    {
        get { return "Rss Provider for the Generic Content Module"; }
    }
}

You are also going to need to create a RssSettings class, but instead of inheriting, we’ll be implementing the IRssSetings interface. This class also needs to contain within it a ControlContainer class. This container class is the link between this settings class and the actual ascx user control that renders on the admin webpage.

using System;
using System.IO;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Telerik.Cms.Engine;
using Telerik.Cms.Engine.Configuration;
using Telerik.Cms.Engine.Security;
using Telerik.Cms.Web.UI;
using Telerik.Security.Permissions;
using Telerik.Framework.Rss;
using Telerik.Rss;

public class GenericContentRssSettings : CompositeControl, IRssSettingsControl{
    private PropertyDescriptorCollection properties;
    private PropertyDescriptorCollection Properties
    {
        get {
            if (this.properties == null)
            {
                this.properties = TypeDescriptor.GetProperties(this);
            }
            return this.properties;
        }
    }

    private ITemplate controlTemplate;
    private ControlContainer ctrlContainer;

    public string ControlTemplatePath
    {
        get {
            object obj2 = this.ViewState["ControlTemplatePath"];
            if (obj2 == null)
            {
                return "~/Sitefinity/Admin/ControlTemplates/Generic_Content/RssSettingsControlTemplate.ascx";
            }
            return (string)obj2;
        }
        set {
            this.ViewState["ControlTemplatePath"] = value;
        }
    }

    private IDictionary<string, string> settings;
    public void InitSettings(IDictionary<string, string> settings)
    {
        this.settings = settings;
    }

    #region IRssSettingsControl Members

    public IDictionary<string, string> SaveSettings()
    {
        settings = new Dictionary<string, string>();
        return settings;
    }

    #endregion protected class ControlContainer : GenericContainer<GenericContentRssSettings>
    {
        public ControlContainer(GenericContentRssSettings owner) : base(owner) { }

    }
}

In addition to these basic classes, we also need the front-end editor web control for editing feed settings in Sitefinity. This is basically a User Control that contains the various input fields for the Rss Feed settings we want to be able to modify, including Title, Description, number of items, etc. This is also the User Control that is referenced in the ControlTemplatePath property shown above.

I used the Blogs Rss Settings Control (from the /sitefinity/admin/controltemplates/blog folder) as an example, and here is what I came up with. Notice how I’ve included a dropdownlist control so that you can select the data from the specific available content providers.

<div class="RssGenericContentChannels"> <ul> <li runat="server" id="providerNameLi"> <label>Content Provider</label> <asp:DropDownList runat="server" ID="ProviderList"></asp:DropDownList> </li> <li class="selector clearfix"> <asp:Label ID="Label8" runat="server" AssociatedControlID="postUrl"> <asp:Literal ID="Literal1" runat="server" Text="Post Url" ></asp:Literal> <cc1:LabelToolTip HelpBoxCssClass="HelpBox" id="labelHelpBox3" runat="server" ToolTipTitle="" ToolTipText="" AlternateText=""></cc1:LabelToolTip> </asp:Label> <asp:TextBox runat="server" ID="postUrl"></asp:TextBox> <asp:LinkButton runat="server" ID="selectPostUrl" Text="Select Url" CssClass="picker" ></asp:LinkButton> </li> <li class="count clearfix"> <asp:Label ID="Label3" runat="server" Text="Item Count" AssociatedControlID="itemsCount"></asp:Label> <asp:TextBox runat="server" ID="itemsCount" ValidationGroup="feedSettings" Text="15"></asp:TextBox> <em class="quont">items</em> </li> <li class="clearfix"> <asp:Label ID="Label6" runat="server" Text="Syndication Type" AssociatedControlID="syndicationType"></asp:Label> <asp:DropDownList runat="server" ID="syndicationType"></asp:DropDownList> </li> <li runat="Server" id="summaryLengthLi" class="count clearfix"> <asp:Label ID="Label5" runat="server" Text="Summary Length" AssociatedControlID="summaryLength"></asp:Label> <asp:TextBox runat="server" ID="summaryLength" ValidationGroup="feedSettings" Text="500"></asp:TextBox> <em class="quont">symbols</em> </li> <li> <label>Category</label> <asp:DropDownList ID="CategoryList" runat="server" /> </li> </ul></div>

So that’s the basic setup, you need to define the three classes as well as the User Control that contains the input fields for editing the feed settings. In my next post, I’ll discuss and show you the various methods in these classes that need to be overridden to provide access to editing and saving these settings, and finally to create the feed itself.

Stay tuned!

Technorati Tags: ,,,,

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!