SSE 698: Web Development

Project 2 (Summer 2010) - Course Syllabus

by Chris A. Bombardo








Overview

This project has grown greatly in scope since it was originally started. The growth was not necessarily for the class project; however, this project is for a usable application. In project two I will cover in some detail the auto upload pages, manual upload page, Login (Forms Authentication), and the database.

The project is taking on several roles. A couple of the pages are rewrites from VB.net to C#. In addition other page will have added features such as storing some information in a database. The project is a tie in to the application Electronic Bluebook. Electronic Bluebook is a secure testing program which uploads the exam to the server during the exam for backup and one final copy upon completion. The auto upload page is going to be setup to automatically take the exam from Electronic Bluebook without user interaction. The manual upload will accept exams with user interaction – manual upload is a backup in case the automatic upload did not work.

During the upload process the page will write some of the exam information to a database. The database along with storing the uploaded exam information will be the bases of a much bigger application, so there will be some connections not covered. In this project the upload will store the exam filename, exam takers ID, time, class name, and class number.

The last part project 2 will cover is the start of the Electronic Bluebook management system. The management system will allow users to login and depending on their permissions perform different task. The login is going to be controlled by Forms authentication with the installer having the option to user a database (above database) or Active Directory to validate users.

Resources

I have used a large variety of sources for this project. I have listed them throughout the document.:

Auto Upload

ASP Markup:
The markup is very simple for the auto upload page. The goal if the page is for the user never see it, so there is no reason to create anything to extravagant. The page simply has a input, a submit, and a couple of labels.

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="EBBAUpload.aspx.cs" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" method="post" enctype="multipart/form-data" runat="server">
    <div>
    <p><input id="fileExam" type="file" name="fileExam" runat="server"/> <input id="Submit1" type="submit" value="Submit" name="Submit1" runat="server"/></p>
    <p><asp:textbox id="lblDataLocation" runat="server"></asp:textbox></p>
    <p></p>
    <asp:label id="lblFormFields" runat="server"></asp:label>
    </div>
    </form>
</body>
</html>


C# Code:
using System;
using System.IO;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
public partial class _Default : System.Web.UI.Page
{

Since this page will not have any user interation it needs to run on load. So, the page is called in Page_Load.

    protected void Page_Load(object sender, EventArgs e)
    {
 
 
PreparePost();
    }
 
const string  NL = "   <BR/>" + "\r\n";
 
    private void PreparePost()
    {
    try {
        Response.Write(NL + "EBB Webserver attempting to Save Posted Exam: " + DateTime.Now.ToString() + NL + NL);
 
 
        DisplayASPData();


This reports if the file did not transfer to the asp page to start with in other words no file name was passed. Even though the users would never see any of the code displayed in ASPData above, I still display it all for several reasons. First it is nice for building the page I could see everything that was happening, since it is pasted back to the program so Electronic Bluebook could take the data and log and respond, finally I added an option to turn on or off the the ability to save to display for live debugging.

if (fileExam.PostedFile == null) {
 
Response.Write("Failure:  File Was Not Received in HTTP Request.");
            DisplayASPData();
 
        }
 
        if (fileExam.PostedFile.ContentLength == 0) {
            Response.Write("Failure: File is size 0.  Hence nothing was saved." + NL);
            DisplayASPData();
 
        }
 
        Response.Write("Received File '" + fileExam.PostedFile.FileName + "', attempting to save it..." + NL);

In the next portion I will have long runs of code. I tried something new as to write what each section of code is doing as a comment in the code. This made it easy to write the report and it made it seem like it was less of doing work twice.

        // Name of Semester folder on server under the web page.
        string SemesterFolder = lblDataLocation.Text;
        //variable for the start position for parsing the course
        int Startpos = 0;
        //variable for the length of the course
        int Lengthpos = 0;
        //variable for the name of the course
        string strCourse = null;
        //variable for the file uploaded this is transfered from teh HTML upload
        string fn = System.IO.Path.GetFileName(fileExam.PostedFile.FileName);
        string strID = null;
 
        string strext = fn.Substring(fn.Length - 3, 3);
 
//validate it is the correct file type.
        if (strext == "exm" | strext == "enc") {
        } else {
            return;
        }
 
        //getting the integer of where the -- is for we can parse the course name from the file name
        Startpos = fn.IndexOf("--");
 
        if (Startpos < 1) {
            //If we are unable to find -- then lets try and post the file to the default semester folder
            //Response.Write("Unable to Parse FileName, Posting to default folder." + NL)
            PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, "UNKNOWN", "UNKNOWN");
 
        }
 
         strID = fn.Substring(0, Startpos - 1);
 
        //getting the integer of where the second set of -- is
        //for we can get length of the course name
         Lengthpos = fn.IndexOf("--", Startpos + 1);
 
        if (Lengthpos == Startpos) {
            // if here then we found the same '--' twice
            //If we are unable to find a second -- then lets try and post the file to the default semester folder
            //Response.Write("Unable to Parse FileName, Posting to default folder." + NL)
            PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, "UNKNOWN", "UNKNOWN");
 
        }
 
        //Parsing the course name from the folder name startpos is the beginning of -- so we add 2 to get to
        //the beginning of the corse name.  Then next step is the length which is the ending -- minus the
        //startpos and extra --
        strCourse = fn.Substring(Startpos + 2, Lengthpos - Startpos - 2);
 
 
        //Response.Write("Course: " & strCourse + NL)
        //Check to see if the folder for the corse exist -- fo to Function FolderExist (below)
        if (FolderExist(Server.MapPath(SemesterFolder) + "\\" + strCourse)) {
            //If it does exist we have our save location and can move to the Sub PostExam
            if (strext == "exm") {
                if (FolderExist(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup")) {
                    PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup\\" + fn, strCourse, strID);
                } else {
                    if (CreateFolder(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup")) {
                        //The course folder creation returned True so we have a save location move to to the Sub PostExam
                        PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup\\" + fn, strCourse, strID);
                    } else {
                        //We could not create the folder and it does not exist so we attempt to save the file in
                        //the default semester folder --Remember to check this folder for exams when decrypting
                        //Response.Write("Unable to Create Course Folder, writing to default instead." + NL)
                        PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, strCourse, strID);
                    }
                }
            } else {
                PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\" + fn, strCourse, strID);
            }
        } else {
            //If the course folder does not exist we go to Function CreateFolder (below)
            //Response.Write("Creating Course Folder: " & SemesterFolder + NL)
            if (CreateFolder(Server.MapPath(SemesterFolder) + "\\" + strCourse)) {
                //The course folder creation returned True so we have a save location move to to the Sub PostExam
                if (strext == "exm") {
                    if (FolderExist(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup")) {
                        PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup\\" + fn, strCourse, strID);
                    } else {
                        if (CreateFolder(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup")) {
                            //The course folder creation returned True so we have a save location move to to the Sub PostExam
                            PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup\\" + fn, strCourse, strID);
                        } else {
                            //We could not create the folder and it does not exist so we attempt to save the file in
                            //the default semester folder --Remember to check this folder for exams when decrypting
                            Response.Write("Unable to Create Course Folder, writing to default instead." + NL)
                            PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, strCourse, strID);
                        }
                    }
                } else {
                    PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\" + fn, strCourse, strID);
                }
            } else {
                //We could not create the folder and it does not exist so we attempt to save the file in
                //the default semester folder --Remember to check this folder for exams when decrypting
                //Response.Write("Unable to Create Course Folder, writing to default instead." + NL)
                PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, strCourse, strID);
            }
        }
 
    } catch (Exception ex) {
        Response.Write("Runtime Exception during [PreparePost]: " + NL + "'" + ex.Message + "'" + NL + "[" + ex.StackTrace.ToString() + "]");
    }
}
 
private void PostExam(string strLocation, string strCourse, string strID)
{
    Response.Write("Saving Exam To: " + strLocation + NL);
    try {
        //Attempt to post the exam to the Save location
 
        fileExam.PostedFile.SaveAs(strLocation);
        Response.Write(NL + " SUCCESS " + NL);
 
    } catch (Exception Ex) {
        //Catch and errors (EBB does this and reports failure to the student you could also
        //write a log with any errors.
        Response.Write("Runtime Exception during [PostExam]: " + NL + "'" + Ex.Message + "'" + NL + "[" + Ex.StackTrace.ToString() + "]");
    }
}
 
//Function to check if class folder exist(strFolder is the passed in value of the path and folder)
private bool FolderExist(string strFolder)
{
    //Validate the saving foder exist.  If it does not it passes
    // false back then goes to createFolder.  It will then again validate
    // the folder exist.
 
    if (Directory.Exists(strFolder))
    {
        return true;
    }
    else
    {
        return false;
    }
}
 
private bool CreateFolder(string strFolder)
{
    try
    {
        Directory.CreateDirectory(strFolder);
        return true;
 
    catch (Exception ex)
    {
        Console.Write(ex);
        return false;
    }
}
 

The next section of code is the displayASPData. This is basically everything I could think I would ever need to debug any problem with uploading a file – it post it back to the website after a upload try. It is probably more information than I will ever need or ever use, but I thought lets see what all I can get. In the future I will need to change it to a debug mode and not post all the information. Some of the information is still read and used by the application posting.


    private void DisplayASPData()
{
    int iIdx = 0;
    string[] fileNames = null;
    HttpFileCollection Files = default(HttpFileCollection);
    int iKeyIdx = 0;
    int iValueIdx = 0;
    string[] strKeys = null;
    string[] strValues = null;
    System.Collections.Specialized.NameValueCollection oHeaders = null;
    System.Collections.Specialized.NameValueCollection oForms = null;
    System.Collections.Specialized.NameValueCollection oQueries = null;
    System.Collections.Specialized.NameValueCollection oServerVars = null;
    System.IO.Stream str = null;
    string strmContents = null;
    int counter = 0;
    int strLen = 0;
    int strRead = 0;
 
    Response.Write(NL + "ASP Debug Data");
    Response.Write(NL + "------------------------------" + NL);
    Response.Write("Time " + DateTime.Now.ToString() + NL);
    Response.Write("Content Encoding: " + Request.ContentEncoding.EncodingName + NL);
    Response.Write("Content Length: " + Request.ContentLength.ToString() + NL);
    Response.Write("Content Type: " + Request.ContentType + NL);
    Response.Write("Current Execution Path: " + Request.CurrentExecutionFilePath + NL);
    Response.Write("-----" + NL);
    Response.Write("Files:" + NL);
    Files = Request.Files;
 
    // Load File collection into HttpFileCollection variable.
    fileNames = Files.AllKeys;
    // This will get names of all files into a string array.
    for (iIdx = 0; iIdx <= fileNames.GetUpperBound(0); iIdx++) {
        Response.Write("    File: " + Server.HtmlEncode(fileNames[iIdx]) + NL);
    }
    Response.Write("-----" + NL);
    Response.Write("Http Method: " + Request.HttpMethod + NL);
    Response.Write("-----" + NL);
    Response.Write("Headers:" + NL);
    oHeaders = Request.Headers;
    strKeys = oHeaders.AllKeys;
    for (iKeyIdx = 0; iKeyIdx <= strKeys.GetUpperBound(0); iKeyIdx++) {
        Response.Write("    Key: " + strKeys[iKeyIdx] + NL);
        strValues = oHeaders.GetValues(iKeyIdx);
        // Get all values under this key.
        for (iValueIdx = 0; iValueIdx <= strValues.GetUpperBound(0); iValueIdx++) {
            Response.Write("         Value " + Convert.ToString(iValueIdx) + ": " + Server.HtmlEncode(strValues[iValueIdx]) + NL);
        }
    }
    Response.Write("-----" + NL);
    Response.Write("-----" + NL);
    Response.Write("Forms:" + NL);
    oForms = Request.Form;
    strKeys = oForms.AllKeys;
    for (iKeyIdx = 0; iKeyIdx <= strKeys.GetUpperBound(0); iKeyIdx++) {
        Response.Write("Form: " + strKeys[iKeyIdx].ToString() + NL);
    }
    Response.Write("-----" + NL);
 
    Response.Write("-----" + NL);
    Response.Write("Queries:" + NL);
    oQueries = Request.QueryString;
    strKeys = oQueries.AllKeys;
    for (iKeyIdx = 0; iKeyIdx <= strKeys.GetUpperBound(0); iKeyIdx++) {
        Response.Write("     Key: " + strKeys[iKeyIdx] + NL);
        strValues = oQueries.GetValues(iKeyIdx);
        for (iValueIdx = 0; iValueIdx <= strValues.GetUpperBound(0); iValueIdx++) {
            Response.Write("      Value " + Convert.ToString(iValueIdx) + ": " + strValues[iValueIdx] + NL);
        }
    }
    Response.Write("-----" + NL);
    Response.Write("-----" + NL);
    Response.Write("ServerVars:" + NL);
    oServerVars = Request.ServerVariables;
    strKeys = oServerVars.AllKeys;
    for (iKeyIdx = 0; iKeyIdx <= strKeys.GetUpperBound(0); iKeyIdx++) {
        Response.Write("    Key: " + strKeys[iKeyIdx] + NL);
        strValues = oServerVars.GetValues(iKeyIdx);
        // Get all values under this key.
        for (iValueIdx = 0; iValueIdx <= strValues.GetUpperBound(0); iValueIdx++) {
            Response.Write("     Value " + Convert.ToString(iValueIdx) + ": " + strValues[iValueIdx] + NL);
        }
    }
    Response.Write("-----" + NL);
 
    Response.Write("Is authenticated :" + Request.IsAuthenticated.ToString() + NL);
    Response.Write("Raw URL :" + Request.RawUrl + NL);
    Response.Write("Request Type : " + Request.RequestType + NL);
    Response.Write("Total Bytes : " + Request.TotalBytes.ToString() + NL);
    Response.Write("\r\n" + "xxxxxxxxxxxxxxx" + "\r\n");
    Response.Write("Binary String" + NL);
    str = Request.InputStream;
    strLen = Convert.ToInt32(str.Length);
    byte[] strArr = new byte[strLen + 1];
    strRead = str.Read(strArr, 0, strLen);
    for (counter = 0; counter <= strLen - 1; counter++) {
        strmContents = strmContents + strArr[counter].ToString();
    }
    Response.Write("\r\n" + "xxxxxxxxxxxxxxx" + "\r\n");
 
 
    //Uncomment 'saveas' code below to view the raw data posted from the client
    //Very useful for debugging clients
    //Just compare file from a client that works with the file from a client that doesn't
    //Request.SaveAs("C:\inetpub\wwwroot\UploadFile\test.txt", True)
 
    Response.Write(NL + "End of ASP Debug Data" + NL + "------------------------------" + NL);
}
 
}
 

As you can see above I create a saveas where I could test the file and see how what I was posting without having to look at the page, this is for when I test with the posting application. I do need to change this to the app path so it does not save the data in a static path. This could be nice if a deployment elsewhere is having issues.

Image of output:
image5.jpg

Manual Upload


Manual Markup

Manual upload asp markup:
The asp code is actually pretty simple again. I did not spend much time on any styles, I spend nearly all of the time on planning and code. Since this portion of the code was a rewrite of some old VB I was able to get the css from that. Jonathan Davis wrote the original asp code for the save. I thought it was important to show, because the coding is little. Even though this is the portion a user may see it is very simple – nothing flashing. It simple contains 5 different styles, a form, table, several labels, input box, and a submit button.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="EBBMUpload.aspx.cs" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<style type="text/css">
<!--
.style1 {
    font-family: Arial, Helvetica, sans-serif;
    font-size: 16px;
    font-weight: bold;
}
.style2 {font-family: Arial, Helvetica, sans-serif; font-size: 24px; font-weight: bold; }
.style3 {
    font-family: Arial, Helvetica, sans-serif;
    font-size: 12px;
    font-weight: bold;
}
.style4 {font-size: 12px; font-family: Arial, Helvetica, sans-serif;}
.style7 {font-family: Arial, Helvetica, sans-serif; font-size: 12px; font-weight: bold; color: #FF0000; }
-->
</style>
 
 
<html xmlns="http://www.w3.org/1999/xhtml">
 
<head runat="server">
    <title></title>
</head>
 
 
<body>
<form id="Form1" method="post" enctype="multipart/form-data" runat="server">
    <p align="center" class="style2">Electronic Bluebook Manual Upload Page </p>
    <p align="center" class="style1">You have been directed to this page following a YELLOW SCREEN because your exam was not successfully saved to the network. </p>
    <table width="60%" border="5" align="center" cellpadding="2" cellspacing="0" bgcolor="#FFFF66">
      <tr>
        <td><p align="center" class="style3">Please use the form below to manually upload your exam <u>before you leave the exam room:</u> </p>
    <P align="center"><input id="fileExam" type="file" name="fileExam" runat="server"/> <input id="Submit1" type="submit" value="Submit" name="Submit1" runat="server"/></P>
  <P align="center">
        <span class="style7">
    <asp:Label id="lblResults" runat="server"></asp:Label></span></P></td>
      </tr>
    </table>
    <p align="center" class="style1">You can easily follow the instructions below to upload your exam without assistance: </p>
    <p align="center" class="style3">
        <asp:TextBox ID="lblDataLocation" runat="server" Visible="False"></asp:TextBox>
    </p>
 
  <P class="style3">To upload your exam:</P>
  <ol>
    <li class="style4">Click on the "Browse" button above.</li>
    <li class="style4">Browse to
      <strong>My Documents</strong>. Click inside the <strong>My Exams</strong> folder and then the <strong>SP2007</strong> folder. </li>
    <li class="style4">Select the exam file which you wish to upload
        (exam files can be identified by EXAM-ID--CourseID-DATE) with .enc and click
      Open or OK.&nbsp; </li>
    <li class="style4">Once the file name is in the textbox click Submit. </li>
  </ol>
  <P class="style3">If you have any problems locating the correct exam, please notify and IT representative. </P>
 
</form>
</body>
</html>

The code behind the manual upload page is nearly identical to the automatic upload page. The only differences are it does not display the asp data and it reports success or failure upload a little more pretty. Other than that it operates the same. In fact a large portion of the code is copied and pasted from the automatic upload.

C# Code:

using System;
using System.IO;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 
    }
 
    private void PreparePost()
{
    try
    {
 
        if (fileExam.PostedFile == null)
        {
            //Response.Write("Failure:  File Was Not Received in HTTP Request.")
 
        }
 
        if (fileExam.PostedFile.ContentLength == 0)
        {
            //Response.Write("Failure: File is size 0.  Hence nothing was saved." + NL)
 
        }
 
        //Name of Semester folder on server under the web page
        string SemesterFolder = lblDataLocation.Text;
        //variable for the start position for parsing the course
        int Startpos = 0;
        //variable for the length of the course
        int Lengthpos = 0;
        //variable for the name of the course
        string strCourse = null;
        //variable for the file uploaded this is transfered from teh HTML upload
        string fn = System.IO.Path.GetFileName(fileExam.PostedFile.FileName);
        //Examinees ID number
        string strID = null;
 
 
        string strext = fn.Substring(fn.Length - 3, 3);
 
        if (strext == "exm" | strext == "enc")
        {
        }
        else
        {
            return;
        }
 
        //getting the integer of where the -- is for we can parse the course name from the file name
        Startpos = fn.IndexOf("--");
 
        if (Startpos < 1)
        {
            //If we are unable to find -- then lets try and post the file to the default semester folder
            //Response.Write("Unable to Parse FileName, Posting to default folder." + NL)
            PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, "UNKNOWN", "UNKNOWN");
 
        }
 
        strID = fn.Substring(0, Startpos - 1);
 
 
        //getting the integer of where the second set of -- is (InStrRev starts at the end of the file)
        //for we can get length of the course name
 
        Lengthpos = fn.IndexOf("--", Startpos + 1);
 
        if (Lengthpos == Startpos)
        {
            // if here then we found the same '--' twice
            //If we are unable to find a second -- then lets try and post the file to the default semester folder
            //Response.Write("Unable to Parse FileName, Posting to default folder." + NL)
            PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, "UNKNOWN", "UNKNOWN");
 
        }
 
        //Parsing the course name from the folder name startpos is the beginning of -- so we add 2 to get to
        //the beginning of the corse name.  Then next step is the length which is the ending -- minus the
        //startpos and extra --
 
        strCourse = fn.Substring(Startpos + 2, Lengthpos - Startpos - 2);
 
 
        //Response.Write("Course: " & strCourse + NL)
        //Check to see if the folder for the corse exist -- fo to Function FolderExist (below)
        if (FolderExist(Server.MapPath(SemesterFolder) + "\\" + strCourse))
        {
            //If it does exist we have our save location and can move to the Sub PostExam
            if (strext == "exm")
            {
                if (FolderExist(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup"))
                {
                    PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup\\" + fn, strCourse, strID);
                }
                else
                {
                    if (CreateFolder(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup"))
                    {
                        //The course folder creation returned True so we have a save location move to to the Sub PostExam
                        PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup\\" + fn, strCourse, strID);
                    }
                    else
                    {
                        //We could not create the folder and it does not exist so we attempt to save the file in
                        //the default semester folder --Remember to check this folder for exams when decrypting
                        //Response.Write("Unable to Create Course Folder, writing to default instead." + NL)
                        PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, strCourse, strID);
                    }
                }
            }
            else
            {
                PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\" + fn, strCourse, strID);
            }
        }
        else
        {
            //If the course folder does not exist we go to Function CreateFolder (below)
            //Response.Write("Creating Course Folder: " & SemesterFolder + NL)
            if (CreateFolder(Server.MapPath(SemesterFolder) + "\\" + strCourse))
            {
                //The course folder creation returned True so we have a save location move to to the Sub PostExam
                if (strext == "exm")
                {
                    if (FolderExist(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup"))
                    {
                        PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup\\" + fn, strCourse, strID);
                    }
                    else
                    {
                        if (CreateFolder(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup"))
                        {
                            //The course folder creation returned True so we have a save location move to to the Sub PostExam
                            PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\backup\\" + fn, strCourse, strID);
                        }
                        else
                        {
                            //We could not create the folder and it does not exist so we attempt to save the file in
                            //the default semester folder --Remember to check this folder for exams when decrypting
                            //Response.Write("Unable to Create Course Folder, writing to default instead." + NL)
                            PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, strCourse, strID);
                        }
                    }
                }
                else
                {
                    PostExam(Server.MapPath(SemesterFolder) + "\\" + strCourse + "\\" + fn, strCourse, strID);
                }
            }
            else
            {
                //We could not create the folder and it does not exist so we attempt to save the file in
                //the default semester folder --Remember to check this folder for exams when decrypting
                //Response.Write("Unable to Create Course Folder, writing to default instead." + NL)
                PostExam(Server.MapPath(SemesterFolder) + "\\" + fn, strCourse, strID);
            }
        }
    }
    catch (Exception ex)
    {
        Console.Write(ex);
        //Response.Write("Runtime Exception during [PreparePost]: " & NL & "'" & ex.Message & "'" & NL & "[" & ex.StackTrace.ToString() & "]")
    }
}
 
    private void PostExam(string strLocation, string strCourse, string strID)
    {
        //Response.Write("Saving Exam To: " + strLocation + NL)
        try
        {
            //Attempt to post the exam to the Save location
            //You could write a log of successful save if you wanted to
 
            fileExam.PostedFile.SaveAs(strLocation);
 
        }
        catch (Exception ex)
        {
            Console.Write(ex);
            //Catch and errors (EBB does this and reports failure to the student you could also
            //write a log with any errors.
            //Response.Write("Runtime Exception during [PostExam]: " & NL & "'" & Ex.Message & "'" & NL & "[" & Ex.StackTrace.ToString() & "]")
        }
 
//************************************************
// Tried to hightlight this section because it is the difference.  It is
// there report back to the user that the upload was successful/
//************************************************
 
  DateTime Now = DateTime.Now;
        lblResults.Text = "SUCCESS: Exam ID " + strID + " successfully upload exam file " + System.IO.Path.GetFileName(fileExam.PostedFile.FileName) + " for " + strCourse + " on " + Now;
    }
 
    //Function to check if class folder exist(strFolder is the passed in value of the path and folder)
    private bool FolderExist(string strFolder)
    {
 
        if (Directory.Exists(strFolder))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
 
    private bool CreateFolder(string strFolder)
    {
        try
        {
            Directory.CreateDirectory(strFolder);
            return true;
        }
        catch (Exception ex)
        {
            Console.Write(ex);
            return false;
        }
    }
 
 
}

Now the basics are completed. We are able to upload the exam both automatically and manually. Next is to add the database. Once the database and the datacontrol class are added then we can modify the code to write to the database for a successful upload. The plan I set for to do this is first to create a table to handle the information. This is where the scope of the project started to unravel. I thought if I am going to store this data then I can store all this other data. In fact it turned into a management system. It now includes release exams, exam information, exam options and so forth. This is one of the biggest problems I create for myself every time I do a project. It will start in defined guidelines, but before I know it I am adding features here and there.

Anyways, I did get myself back on track, after creating a much larger than anticipated database. The tables are in the image below to the left. So the first part I need to do here was write a database control class I could call from anywhere to read and write to the database. I did plan on using the database in several areas. So I created dataControl class.

Image of page:

image2.jpg

Database


The dataControl class is fairly simple. First to create a connection string. To do so I wanted to store the data external of the application for it could be deployed anywhere, so I created a cRegistry class to read from the registry. This way I could store the server name along with any other information in the registry making it easy to deploy in different environments.
Since the registry was an add-in I will just highlight the read section. However, I will admit this set me back time was substantial. I had a lot of trouble reading from HKey Local Machine, in fact you will notice currently I am reading from CurrentUser. This will have to be changed, however, I believe I will have to first sign the application and elevate the program to run as administrator. This is because I am working on a Windows 7 machine.

Registry


    private string subKey = "SOFTWARE\\***********\\";
    private RegistryKey baseRegistryKey = Registry.CurrentUser;
 
   public string Read(string KeyName)
    {
        RegistryKey pKey = baseRegistryKey;
        RegistryKey sKey = pKey.OpenSubKey(subKey);
        if (sKey == null)
        {
            return null;
        }
        else
        {
            try
            {
                // If the RegistryKey exists I get its value
                // or null is returned and handle the error.
                return (string)sKey.GetValue(KeyName.ToUpper());
            }
            catch (Exception e)
            {
                Console.Write(e);
                return null;
            }
        }
 
    }

Now the registry class is completed and working back to the dataControl.

dataControl Code


using System;
using System.Collections.Generic;
using System.Web;
using System.Data.SqlClient;
 
/// <summary>
/// Summary description for dataControl
/// </summary>
public class dataControl
{
 
    private SqlConnection myConnection;
    private cRegistery getRegValues;
 
    public dataControl()
    {
 

First we need to setup a connection string. As you can see we are pulling our information from the registry.

        cRegistery getRegValues = new cRegistery();
 
        string username = getRegValues.Read("SQLUsername");
        string password = getRegValues.Read("SQLPassword");
        string serverurl = getRegValues.Read("SQLServer");
        string trusted = getRegValues.Read("Trusted_Connection");
 
        myConnection = new SqlConnection("user id=" + username +
                                           ";password=" + password + ";server=" + serverurl +
                                           ";Trusted_Connection=" + trusted + ";" +
                                           "database=EBBData; Data Source=.\\SQLEXPRESS" +
                                           "connection timeout=30");
    }

Next we need to open out data connection.

    public void Open_Data_Con()
    {
        try
        {
            myConnection.Open();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
 

Now we can either call to write to the database. Which with the Sub below we can simple pass the command and write to the database. In the next section (Login) we will see this code called.

    public void sqlCommand(string sWriteCommand)
    {
        SqlCommand myCommand = new SqlCommand(sWriteCommand, myConnection);
        myCommand.ExecuteNonQuery();
    }

Below is the read of the database. It simple passes the command and returns a single string. Notice it is a single string, currently I am not passing or using a dataset. Once that is needed I will have another Sub for a dataset.

    public string ReadSQL(string sReadCommand)
    {
        try
        {
            SqlDataReader myReader = null;
            SqlCommand myCommand = new SqlCommand(sReadCommand, myConnection);
            string pwd = ""; // I used pwd first because it started for password
            myReader = myCommand.ExecuteReader();
            while (myReader.Read())
            {
                pwd = Convert.ToString(myReader.GetString(0));
            }
            return pwd;
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
            return null;
 
        }
    }
}
 

That completed the dataControl. It is fairly simple. However, as the project goes forward I can see the dataControl growing because of calling datasets and particular tables.

Image1.jpg

The final portion of this project is the login. The login I wanted to use the Forms Authentication, however, I wanted the ability to tie the authentication into Active Directory. So I have used Visual Studios login template and slightly modified it. The modifications include a registry setting (same cRegistry class from above) to get if the authentication is going to be database or AD. Next, I validate the user. Once validated I direct them to the proper site based on their permissions (I am still working on this).

Login (Forms Authentication)

– The asp mark-up is the Microsoft login template.

Markup


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="EBBLogon.aspx.cs"
Inherits="adminLogin" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
 
        <asp:Login ID="Login1" runat="server" BackColor="#F7F6F3" BorderColor="#E6E2D8"
            BorderPadding="4" BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana"
            Font-Size="0.8em" ForeColor="#333333">
            <TextBoxStyle Font-Size="0.8em" />
            <LoginButtonStyle BackColor="#FFFBFF" BorderColor="#CCCCCC" BorderStyle="Solid"
                BorderWidth="1px" Font-Names="Verdana" Font-Size="0.8em" ForeColor="#284775" />
            <LayoutTemplate>
                <table border="0" cellpadding="4" cellspacing="0"
                    style="border-collapse:collapse;">
                    <tr>
                        <td>
                            <table border="0" cellpadding="0">
                                <tr>
                                    <td align="center" colspan="2"
                                        style="color:White;background-color:#5D7B9D;font-size:0.9em;font-weight:bold;">
                                        EBB Log In</td>
                                </tr>
                                <tr>
                                    <td align="right">
                                        <asp:Label ID="UserNameLabel" runat="server" AssociatedControlID="UserName">User Name:</asp:Label>
                                    </td>
                                    <td>
                                        <asp:TextBox ID="UserName" runat="server" Font-Size="0.8em"></asp:TextBox>
                                        <asp:RequiredFieldValidator ID="UserNameRequired" runat="server"
                                            ControlToValidate="UserName" ErrorMessage="User Name is required."
                                            ToolTip="User Name is required." ValidationGroup="Login1">*</asp:RequiredFieldValidator>
                                    </td>
                                </tr>
                                <tr>
                                    <td align="right">
                                        <asp:Label ID="PasswordLabel" runat="server" AssociatedControlID="Password">Password:</asp:Label>
                                    </td>
                                    <td>
                                        <asp:TextBox ID="Password" runat="server" Font-Size="0.8em" TextMode="Password"></asp:TextBox>
                                        <asp:RequiredFieldValidator ID="PasswordRequired" runat="server"
                                            ControlToValidate="Password" ErrorMessage="Password is required."
                                            ToolTip="Password is required." ValidationGroup="Login1">*</asp:RequiredFieldValidator>
                                    </td>
                                </tr>
                                <tr>
                                    <td colspan="2">
                                        <asp:CheckBox ID="RememberMe" runat="server" Text="Remember me next time." />
                                    </td>
                                </tr>
                                <tr>
                                    <td align="center" colspan="2" style="color:Red;">
                                        <asp:Literal ID="FailureText" runat="server" EnableViewState="False"></asp:Literal>
                                    </td>
                                </tr>
                                <tr>
                                    <td align="right" colspan="2">
                                        <asp:Button ID="LoginButton" runat="server" BackColor="#FFFBFF"
                                            BorderColor="#CCCCCC" BorderStyle="Solid" BorderWidth="1px" CommandName="Login"
                                            Font-Names="Verdana" Font-Size="0.8em" ForeColor="#284775" Text="Log In"
                                            ValidationGroup="Login1" onclick="LoginButton_Click" />
                                    </td>
                                </tr>
                            </table>
                        </td>
                    </tr>
                </table>
            </LayoutTemplate>
            <InstructionTextStyle Font-Italic="True" ForeColor="Black" />
            <TitleTextStyle BackColor="#5D7B9D" Font-Bold="True" Font-Size="0.9em"
                ForeColor="White" />
        </asp:Login>
 
    </div>
    </form>
</body>
</html>

Login Code:


using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
 
public partial class adminLogin : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 
    }
 
    cRegistery mycRegistry = new cRegistery();
    dataControl mydataControl = new dataControl();
 

The first part once the login button is clicked is to read the registry and determine the authenticationType.

    protected void LoginButton_Click(object sender, EventArgs e)
    {
        if (Convert.ToInt32(mycRegistry.Read("authenticationType")) == 1)
        {
            authAD();
        }
        else
        {
            authDB();
        }
    }
 

If it Active Directory then we create an instance of authenticationAD. Pass the username and password to the authenticationAD class and receive a true or false on validation. The code for the authenticatioAD follows.

    private void authAD()
    {
        authenticationAD authTypeAD = new authenticationAD(mycRegistry.Read("ADPath"));
 
        if (authTypeAD.IsAuthenticated(mycRegistry.Read("domain"), this.Login1.UserName, this.Login1.Password))
        {
            Authenticated();
        }
        else
        {
            this.Login1.FailureText = "Login failed. Please check your user name and password and try again.";
        }
    }
 

If it database then we create an instance of authenticationDB. Pass the username and password to the authenticationDB class and receive a true or false on validation. The code for the authenticatioDB follows.

    private void authDB()
    {
        authenticationDB authTypeDB = new authenticationDB();
        if (authTypeDB.ValidateUser(this.Login1.UserName, this.Login1.Password))
        {
            Authenticated();
        }
        else
        {
            this.Login1.FailureText = "Login failed. Please check your user name and password and try again.";
        }
    }

If either AD or DB validates the user then the users is passed to the authenticated sub. In this sub a cookie is created to keep the user validated for a period of time. Each time the user visits one of protected pages it checks for the cookie. Once the users cookie is created they are directed to the permission level page.

    private void Authenticated()
    {
        string username = this.Login1.UserName;
        string password = this.Login1.Password;
        bool isPersistent = this.Login1.RememberMeSet;
 
        string userData = "ApplicationSpecific data for this user.";
 
        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, DateTime.Now
            , DateTime.Now.AddMinutes(30), isPersistent, userData, FormsAuthentication.FormsCookiePath);
 
        // Encrypt the ticket.
        string encTicket = FormsAuthentication.Encrypt(ticket);
 
        // Create the cookie.
        Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
 
        // Redirect back to original URL.
 
        mydataControl.Open_Data_Con();
 
        switch (Convert.ToInt32(mydataControl.ReadSQL("Select accountType from users where username ='" + this.Login1.UserName + "'")))
        {
            case 1: //Student
                Response.Redirect("student//ebbmain.aspx");
                break;
 
            case 2: //Faculty
                Response.Redirect("admin//ebbmain.aspx");
                break;
 
            case 3: //Admin
                Response.Redirect("admin//ebbmain.aspx");
                break;
 
            default: //Unknown
                break;
        }
 
    }
}
 


Login screen:
image3.jpg

Failed login:
image4.jpg



Following is the code to validate a user with AD followed by code to validate in database. Both of these codes were from samples on MSDN, with slight modifications. Since the code is taken directly from Microsoft I will provide little comment.

Authenticate AD

using System;
using System.Collections.Generic;
using System.Web;
using System.Text;
using System.Collections;
using System.DirectoryServices;
 
/// <summary>
/// Summary description for authenticationAD
/// </summary>
public class authenticationAD
{
 
    private String _path;
    private String _filterAttribute;
 
    public authenticationAD(String path)
    {
      _path = path;
    }
 
    public bool IsAuthenticated(String domain, String username, String pwd)
    {
      String domainAndUsername = domain + @"\" + username;
      DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
 
      try
      {    //Bind to the native AdsObject to force authentication.
         Object obj = entry.NativeObject;
 
    DirectorySearcher search = new DirectorySearcher(entry);
 
    search.Filter = "(SAMAccountName=" + username + ")";
    search.PropertiesToLoad.Add("cn");
    SearchResult result = search.FindOne();
 
    if(null == result)
    {
        return false;
    }
 
    //Update the new path to the user in the directory.
    _path = result.Path;
    _filterAttribute = (String)result.Properties["cn"][0];
      }
      catch (Exception ex)
      {
        throw new Exception("Error authenticating user. " + ex.Message);
      }
 
    return true;
     }
 
     public String GetGroups()
     {
       DirectorySearcher search = new DirectorySearcher(_path);
       search.Filter = "(cn=" + _filterAttribute + ")";
       search.PropertiesToLoad.Add("memberOf");
       StringBuilder groupNames = new StringBuilder();
 
       try
       {
         SearchResult result = search.FindOne();
 
     int propertyCount = result.Properties["memberOf"].Count;
 
        String dn;
     int equalsIndex, commaIndex;
 
     for(int propertyCounter = 0; propertyCounter < propertyCount; propertyCounter++)
     {
       dn = (String)result.Properties["memberOf"][propertyCounter];
 
           equalsIndex = dn.IndexOf("=", 1);
       commaIndex = dn.IndexOf(",", 1);
       if(-1 == equalsIndex)
       {
         return null;
            }
 
           groupNames.Append(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1));
       groupNames.Append("|");
 
         }
       }
       catch(Exception ex)
       {
         throw new Exception("Error obtaining group names. " + ex.Message);
       }
       return groupNames.ToString();
     }
   }
 




Authenticate DB

using System;
using System.Collections.Generic;
using System.Web;
using System.Data.SqlClient;
using System.Web.Security;
 
/// <summary>
/// Summary description for authenticationDB
/// </summary>
public class authenticationDB
{
    public authenticationDB()
    {
    }
 
    public bool ValidateUser(string userName, string passWord)
    {
        string lookupPassword = null;
 
        // Check for invalid userName.
        // userName must not be null and must be between 1 and 15 characters.
        if ((null == userName) || (0 == userName.Length) || (userName.Length > 15))
        {
            System.Diagnostics.Trace.WriteLine("[ValidateUser] Input validation of userName failed.");
            return false;
        }
 
        // Check for invalid passWord.
        // passWord must not be null and must be between 1 and 25 characters.
        if ((null == passWord) || (0 == passWord.Length) || (passWord.Length > 25))
        {
            System.Diagnostics.Trace.WriteLine("[ValidateUser] Input validation of passWord failed.");
            return false;
        }
 
        try
        {
            // Consult with your SQL Server administrator for an appropriate connection
            // string to use to connect to your local SQL Server.
 
            dataControl mydataControl = new dataControl();
            mydataControl.Open_Data_Con();
            lookupPassword = (string) mydataControl.ReadSQL("Select password from users where username='" + userName + "'");
 
        }
        catch (Exception ex)
        {
            // Add error handling here for debugging.
            // This error message should not be sent back to the caller.
            System.Diagnostics.Trace.WriteLine("[ValidateUser] Exception " + ex.Message);
        }
 
        // If no password found, return false.
        if (null == lookupPassword)
        {
            // You could write failed login attempts here to event log for additional security.
            return false;
        }
 
        // Compare lookupPassword and input passWord, using a case-sensitive comparison.
        return (0 == string.Compare(lookupPassword, passWord, false));
 
    }
 
}