Recurring Events in ASP.NET

November 12, 2008 09:49 by John M

So I had some trouble finding a good way on how to create a recurring event in asp.net.  After playing around for a bit this is the concept I came up with and it worked quite well. The biggest date range I tested was about 12 months worth.  I was worried about how many iterations this concept would use, but turned out it wasn't that bad.

You figure the average user will have a recurring event 2-3 days a week and at the most 12 months worth. With the method below it would create about a total of 1090 iterations.  This would cause for about 145 DB entries (if you store each event as a seperate record).  Their are different ways to store the events, but I am doing a seperate record right now just because I don't want to have to touch the backend of the DB I'm working with (yes I know poor excuse).

Their isn't any performance hit for this so far.  Then again I don't have it writing to the DB yet either. So with that said if anyone else can come up with a more efficient concept please let me know.


Concept

Have user choose which days of the week the event is on
Have user choose time of the event
Have user choose a date range for the events recurrence

Store event information in a struct
Store the struct in an array
Store the days of the week of the event in an array
  this lets us avoid having to keep iterating the checkBoxList

Keep incrementing current date by 1 day


As usual I'm just going to paste the code that makes this up.  I have comments in their
so any questions let me know.

------------------------------------

events.aspx markup

<form id="form1" runat="server">
    <div>
        <asp:Label ID="Label1" runat="server" Text="Event recurrs which days of the week?"></asp:Label>
        <br />
        <asp:CheckBoxList ID="CheckBoxList1" runat="server"
            RepeatDirection="Horizontal" RepeatLayout="Flow">
            <asp:ListItem>Monday</asp:ListItem>
            <asp:ListItem>Tuesday</asp:ListItem>
            <asp:ListItem>Wednesday</asp:ListItem>
            <asp:ListItem>Thursday</asp:ListItem>
            <asp:ListItem>Friday</asp:ListItem>
            <asp:ListItem>Saturday</asp:ListItem>
            <asp:ListItem>Sunday</asp:ListItem>
        </asp:CheckBoxList>
        <br />
        <br />
        <asp:Label ID="Label3" runat="server" Text="Enter Time of Event:"></asp:Label>
        <br />
       
        <!-- make the text boxes into drop down lists for user -->
        <asp:TextBox ID="txtStartTime" runat="server"></asp:TextBox>
        &nbsp;&nbsp;
        <asp:TextBox ID="txtEndTime" runat="server"></asp:TextBox>
        <br />
        <asp:Label ID="Label2" runat="server" Text="Enter date range for recurring event:"></asp:Label>
        <br />
        <asp:TextBox ID="txtStartDate" runat="server"></asp:TextBox>
        &nbsp;&nbsp;
        <asp:TextBox ID="txtEndDate" runat="server"></asp:TextBox>
        <br /><br />
        <asp:Button ID="Button1" runat="server" Text="Enter Event" onclick="Button1_Click" />
</div>
</form>

--------------------------------------

event.aspx.cs

 //store event details
        protected struct CalEvents
        {
            public string eventDate;
            public string startTime;
            public string endTime;
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            DateTime currDate; //used to keep incrementing days
            ArrayList aryDays = new ArrayList(); //store DayOfWeek value
            ArrayList aryEvents = new ArrayList(); //store array of CalEvents structs

            //TODO: properly parse date and time
            //make time text boxes into drop down lists for user to select
            string startTime = txtStartTime.Text;
            string endTime = txtEndTime.Text;
            DateTime startDateRange = DateTime.Parse(txtStartDate.Text);
            DateTime endDateRange = DateTime.Parse(txtEndDate.Text);
            currDate = DateTime.Now.Date;

            //store days of week event should be on
            for (int i = 0; i < CheckBoxList1.Items.Count; i++)
            {
                if (CheckBoxList1.Items[i].Selected)
                {
                    switch(CheckBoxList1.Items[i].Value)
                    {
                        case "Sunday":
                            aryDays.Add(DayOfWeek.Sunday);
                            break;
                        case "Monday":
                            aryDays.Add(DayOfWeek.Monday);
                            break;
                        case "Tuesday":
                            aryDays.Add(DayOfWeek.Tuesday);
                            break;
                        case "Wednesday":
                            aryDays.Add(DayOfWeek.Wednesday);
                            break;
                        case "Thursday":
                            aryDays.Add(DayOfWeek.Thursday);
                            break;
                        case "Friday":
                            aryDays.Add(DayOfWeek.Friday);
                            break;
                        case "Saturday":
                            aryDays.Add(DayOfWeek.Saturday);
                            break;
                        default:
                            break;
                    }
                }
            }

            //TODO: add logic to check and make sure startDateRange is
            //equal or greater than today's date
            //Also check to make sure endDateRange is no more than 12 months out
            while ((currDate >= startDateRange) && (currDate <= endDateRange))
            {
                CalEvents evt = new CalEvents();

                for (int i = 0; i < aryDays.Count; i++)
                {
                    if (currDate.DayOfWeek == (DayOfWeek)aryDays[i])
                    {
                        evt.eventDate = currDate.Date.ToString();
                        evt.startTime = startTime;
                        evt.endTime = endTime;

                        aryEvents.Add(evt);
                    }
                }

                currDate = currDate.AddDays(1.0);
            }


            for (int i = 0; i < aryEvents.Count; i++)
            {
                CalEvents evt = new CalEvents();
                evt = (CalEvents)aryEvents[i];

                //TODO: add logic to write event to database

                //debugging purpose only
                Response.Write(string.Format("Event Info: {0} <br />", evt.eventDate));
            }

        }


Of course I am missing code for all of the user input validation, but you can add that as you choose.  Happy coding.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Categories: ASP.NET
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Upload Multiple Files in ASP.NET

November 7, 2008 11:16 by John M

I have seen a lot of different ways to upload multiple files in ASP.NET but they all consisted of either using a lot of javascript, a pre-determined amount of FileUpload controls on the page, or 3rd part controls that will do it for you. I decided to take a different approach and use bytes to utilize multiple files. 

Now this demonstration is actually showing how to upload multiple photos in ASP.NET, but it can very well be used to upload multiple files as well.

The concept is as follows:

One FileUpload control on the page
One DataList to show which images the user will be uploading
Store image information in a dataset with one of the columns being the image in bytes
User has option to remove image from the upload list if they want before uploading it.

I will simply copy and paste the code for what I have done.  I will mention I have not tested this fully in production.  My development Environment is VS 2008, debugging using built in web server in Visual Studio.

This code is for a file called getImage.aspx.  This returns the image in binary for previewing of which images will be uploaded.  You’ll see the main pages uses this page as the ImageUrl in the DataList. I am only pasting the code behind because there isn’t any markup involved.

getImage.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
 //TODO: error checking for query string

 if(Session["dsPhotoFiles"] != null)
 {
      DataSet tmpDs = new DataSet();
      tmpDs = (DataSet)Session["dsPhotoFiles"];

      for (int i = 0; i < tmpDs.Tables[0].Rows.Count; i++)
      {
         if (tmpDs.Tables[0].Rows[i]["id"].ToString() == Request.QueryString["id"])
         {
             Response.BinaryWrite((byte[])(tmpDs.Tables[0].Rows[i]["fileBytes"]));
             break;
         }
      }
 }
}

 

Below is now the code for the main page used to upload multiple images.

Default.aspx

This is the code inside the form tag

<div>
     <asp:Label ID="Label1" runat="server" Text="Locate File:" />&nbsp;&nbsp;
     <asp:FileUpload ID="fileUp" runat="server" /> &nbsp;&nbsp;
     <br />
     <asp:Label ID="Description" runat="server" Text="Description:"></asp:Label>&nbsp;&nbsp;
     <asp:TextBox ID="txtDesc" runat="server" Width="349px"></asp:TextBox>
     <asp:Button ID="btnAddFile" runat="server" Text="Add File To Upload List"
                onclick="btnAddFile_Click" />
</div>
 
<div>
     <asp:Label ID="lblCurrentFileCount" runat="server"></asp:Label>
     <br />
     <asp:DataList ID="dlPhotoFiles" runat="server" RepeatColumns="10" RepeatDirection="Horizontal">
         <ItemTemplate>
             <asp:Image ID="Image1" runat="server" ImageUrl='<%# string.Format("~/getImage.aspx?id={0}", Eval("id")) %>'
                   Width="50" AlternateText='<%# Eval("fileDesc") %>' />
             <br />
             <asp:LinkButton ID="lnkBtnRemovePhoto" runat="server"
                        CommandArgument='<%# Eval("id") %>' OnCommand="lnkBtnRemovePhoto_Command">Remove</asp:LinkButton>
         </ItemTemplate>                  
      </asp:DataList>
      <br />
</div>
<br />
<div>
     <asp:Button ID="btnUploadFiles" runat="server" Text="Upload File(s)"
                   onclick="btnUploadFiles_Click" />
</div>


Default.aspx.cs

This is all of the code behind:

DataSet dsPhotosFiles = new DataSet();

        protected void Page_Init(object sender, EventArgs e)
        {

            if (Session["dsPhotoFiles"] == null)
            {
                this.initPhotoDS();
                Session.Add("dsPhotoFiles", dsPhotosFiles);
            }
            else
            {
                dsPhotosFiles = (DataSet)Session["dsPhotoFiles"];
            }
        }

        protected void btnAddFile_Click(object sender, EventArgs e)
        {

            if (fileUp.PostedFile.ContentLength > 0)
            {
                //TODO: logic for file extension check
                DataRow dr = dsPhotosFiles.Tables[0].NewRow();

                string fileExt = System.IO.Path.GetExtension(fileUp.PostedFile.FileName);

                byte[] imageBytes = new byte[fileUp.PostedFile.InputStream.Length];
                fileUp.PostedFile.InputStream.Read(imageBytes, 0, imageBytes.Length);

                dr["fileBytes"] = imageBytes;
                dr["filePath"] = fileUp.PostedFile.FileName;
                dr["fileDesc"] = txtDesc.Text;
                dr["id"] = System.Guid.NewGuid().ToString();

                dsPhotosFiles.Tables[0].Rows.Add(dr);
            }

            this.bindDataList();          

            txtDesc.Text = "";
            lblCurrentFileCount.Text = "Current Files To Upload: " + dsPhotosFiles.Tables[0].Rows.Count;
        }

        protected void btnUploadFiles_Click(object sender, EventArgs e)
        {

            try
            {
                for (int i = 0; i < dsPhotosFiles.Tables[0].Rows.Count; i++)
                {
                    //TODO:logic to save image path and description to database
                   
                    string fileName = System.IO.Path.GetFileName(dsPhotosFiles.Tables[0].Rows[i]["filePath"].ToString());

                    byte[] imageBytes;
                    imageBytes = (byte[])dsPhotosFiles.Tables[0].Rows[i]["fileBytes"];

                    //their is no uploading..just writing out the bytes to the directory on the web server.
                    System.IO.File.WriteAllBytes(Server.MapPath(string.Format("~/documents/{0}", fileName)), imageBytes);
                }

                //TODO: show success message logic

                //clear out rows of dataset not the whole dataset
                dsPhotosFiles.Tables[0].Rows.Clear();
                this.bindDataList();
                lblCurrentFileCount.Text = "Current Files To Upload: " + "0";
               
            }
            catch (Exception ex)
            {
                //TODO: show error message of which file did not get uploaded
                throw new Exception(ex.Message);
            }
          
        }

        private void initPhotoDS()
        {
            dsPhotosFiles.Tables.Add("Photos");
            dsPhotosFiles.Tables[0].Columns.Add("fileBytes", Type.GetType("System.Byte[]"));
            dsPhotosFiles.Tables[0].Columns.Add("filePath");
            dsPhotosFiles.Tables[0].Columns.Add("fileDesc");
            dsPhotosFiles.Tables[0].Columns.Add("id");
        }

        private void bindDataList()
        {
            dlPhotoFiles.DataSource = dsPhotosFiles;
            dlPhotoFiles.DataKeyField = "id";
            dlPhotoFiles.DataBind();
        }

        protected void lnkBtnRemovePhoto_Command(object sender, CommandEventArgs e)
        {

            foreach(DataRow dr in dsPhotosFiles.Tables[0].Rows)
            {
                if (dr["id"].ToString() == e.CommandArgument.ToString())
                {
                    dsPhotosFiles.Tables[0].Rows.Remove(dr);
                    break;
                }

            }

            this.bindDataList();
            lblCurrentFileCount.Text = "Current Files To Upload: " + dsPhotosFiles.Tables[0].Rows.Count;
        }

 

Again, I have not tested this fully in product, but it seems to be working very solid right now in my debug environment. 

The one downfall I see to this is that for large photos that could be a lot to store in memory/session.  Now a good thing about this is that it would be very easy to limit the photo/image count a user can upload because you can just check the row count in the dataset and either add it or not to the dataset.

I see a couple of advantages to this:

The image is in memory so their really isn’t any uploading involved because once it’s in memory it’s write it directly to the directy on the web server.
Gives the user the ability to see the photos they are uploading
Gives the user the ability to remove photos before they commit to uploading them
Can write image information to a database if you would like.

As always, I hope this will make it easier for those out their like me who want a decent way to upload multiple files.  Please comment for any bugs found in this.  I will review this over the next couple of days and try in production as well.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Categories: ASP.NET
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

User Authentication in ASP.NET to Prevent Brute Force Attacks

August 22, 2008 16:29 by John M

This article will provide a very simple way to prevent brute force attacks in ASP.NET for whatever authentication method you so choose.  Brute force attacks are when a program will keep feeding usernames and passwords into your login page and hopefully get the right one after thousands and thousands of tries.  I will show you a way to hopefully prevent this, or at least slow it down so it persuades the attacker to hopefully leave your site and move on to the next one. 

First off please always remember to throw a generic message when the username or password is incorrect.  For example a generic error message would be “Incorrect username or password”.  Please do not say “incorrect username” if the password is right and the username is wrong.  Vice versa for the password being correct. This way the attacker doesn’t know what is right or wrong. 

So the method I follow for avoiding this is to sleep the thread on the page if the username or password is incorrect.  I actually sleep the thread anywhere from 2 to 20 seconds.  This means that if the credentials are incorrect it will not respond for 2 to 20 seconds.  It may not seems like a lot, but when a program is trying to feed credentials into the login page…it will have to wait that long to try another one.  This will drastically increase the time the program doing the brute force has to go through. In addition, making the seconds random will also help a bit too.

Now my example is the following scenario.

1.       A CAPTCHA on the web page

2.       Simple username and password text box with customer membership provider

Below is some sample code that is used to check the username and password and also to sleep the thread if it either one is incorrect. This code does not include the code to check the CAPTCHA

aryCreds just actually stores the username and role...that's about it.

protected void lnkBtnLogin_Click(object sender, EventArgs e)
{       
  
ArrayList aryCreds = new ArrayList();        
  
if (users.checkUserAuth(txtUserName.Text, txtPassword.Text))
       
  
{
            
     
aryCreds = users.getUserCreds(txtUserName.Text);           
     
Session.Add("aryCreds", aryCreds);
            
     
Response.Redirect("menu.aspx");
       
  
}        
  
else
       
  
{
           
     
this.delayRequest();
           
     
txtUserName.Text = null;
           
     
txtPassword.Text = null;
            
     
string error = common.ErrorMessage("Incorrect Username or Password");
            
     
this.Page.Controls.Add(new LiteralControl(error));     
        
  
}
 
} 

private void delayRequest()
{       
  
int minSeconds, maxSeconds;
       
  
minSeconds = 2;
       
  
maxSeconds = 20;
       
  
Random rand = new Random();
       
  
System.Threading.Thread.Sleep(rand.Next(minSeconds, maxSeconds) * 1000);

}

 

Notice the delayRequest method. This is getting called if the checkUserAuth method does not return true.  

Also you'll notice the  

Random rand = new Random();
System.Threading.Thread.Sleep(rand.Next(minSeconds, maxSeconds) * 1000);

This is what actually makes the page delay the error message.

Well I hope this has helped some people out there on a way that can help make their page a bit more protected against attackers for a login scenario.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: ASP.NET
Actions: E-mail | Permalink | Comments (3) | Comment RSSRSS comment feed

Compiling Websites in ASP.NET

August 19, 2008 14:10 by John M

I just wanted to share my thoughts on how I go about compiling a website and deploying it to a hosting company.  I personally use Go Daddy as a hosting provider because their tools are excellent and I like their SQL management interface.  One of my favorite tools of theirs is the file manager to set permissions on directories.  In addition, their IIS manager is great as well for creating and using virtual directories.  Oh yea and they are cheap! Ok enough about Go Daddy…

I will use the word “compiling” over publishing because in a sense that is what is going on anyway.  You are compiling your code into multiple dll’s or a single dll.  Below are the options I use for compiling a web project:

Option 1) Select Publish Website within Visual Studio and check the boxes “Allow this precompiled site to be updatable” and “Use fixed naming and single page assemblies”

Option 2) Add a Web Deployment Project and mess around with the options in there.  I usually use this options if I need to compile into one single assembly.  However, this is great for a “commercial” environment as well because it can create an MSI package that will install the website for you. Here is the link to Scott Gu’s blog for Visual Studio 2005

http://weblogs.asp.net/scottgu/archive/2005/11/06/429723.aspx

Here is Scott Gu’s blog regarding the Web Deployment for VS 2008

http://weblogs.asp.net/scottgu/archive/2008/01/28/vs-2008-web-deployment-project-support-released.aspx

Now I’m going to list some reasons why I like option 1 because I don’t have a need most of the time for option 2.  Although our product xLite Player is actually compiled with option 2 so I have single dll.

Option 1 benefits

Makes maintaining a website much much easier. Think about it, if you have a website of 30-40 pages, controls, etc… and you only need to make a change to the code behind on a couple of the pages or just one of them….you are only going to need to upload those 2 dll’s again after compiling the website.  In return, if by some chance you didn’t code something right you only broke those two pages of the site…instead of taking down the entire thing.

You can edit markup language on the fly. Again, this allows for easier maintenance. If you need to make a change to something on a site for text, formatting, sql data sources, etc… you can do it without recompiling the entire site.

It helps with a “commercial” environment. This is actually how the Club Dynamix product is compiled because when we fix bugs, or make enhancements, again, we only need to worry about providing the dll or the page to the customer, or uploading it ourselves for them. 

So those are my biggest benefits to Option 1. You’ll notice it all has to do with maintenance time.  I personally don’t like wasting time on maintenance and wondering if the site will go down by making the change.    

Keep in mind by using Option 1 the page names or dll names never change and that is what makes it possible to only upload one or the other if needed. 

I have found this method to help out the most when dealing with hosting companies as well.  I know this is nothing new, but just wanted to share on how I go about compiling, deploying and maintaining a site when it comes to editing code behind or markup language.


Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Categories: ASP.NET
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

PreLoading Web Pages - Part III

June 27, 2008 13:00 by Onslaught
Previously, we discussed two methods on preloading web pages. You can either switch the display between "divLoading" and "divContent" <div> controls, or use the javascript getHTTPObject. If that makes no sense, then you can read Part I and Part II respectively.

This method does not have the the limitation of the first two. What is the solution? Inline frames. Implementing is similar to Part I.

Create your "loading" <div> control. In this control, you place a message or image you want users to see while your page preloads. Note the styling applied to the control.

<div id="divLoading" style="position:absolute, width:100%; height:100%; background-color:white;">
  <img src="imgLoading.gif" alt="Loading Page" /><br>
  LOADING PAGE...
</div>

Now, create your <iframe> control.

<iframe src="content.aspx" frameborder="0" id="iContent" onload="hideLoading()" />

Your javascript code.

<script type="javascript">
  function hideLoading()
  {
    document.getElementbyId("divLoading").style.display="none";
    document.getElementbyId("divContent").style.display="visible";
  }
</script>

Remember the innovating thinking from part one? It applies here as well. Experiment to determine the best method for your needs. You can even mix the method from Part II to update the source of the inline frame, but keep in mind the cross scripting limitation. Alternatively, you can update the SRC attribute rather that simply hiding the <div> control.

In my experience, using inline frames were difficult to work with. Aligning, positioning and displaying were tricky but once I got the hang of it, it wasn't to bad. I was able to use AJAX controls, and kept my code modular. Maintenance was simplified in that I don't have to touch the core backbone.

Feel free to post your ideas on preloading web pages. The end.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: ASP.NET
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed