User Management Forms

If you have online users, you will likely want the administrator to be able to manage these from within the web application.

In this tutorial we will look at basic user management forms, taking advantage of the default web forms as we go to get something quick and functional (but not ideal).

You should first have created your 'Admin' folder as detailed in the previous tutorial, ensuring it is restricted to only users with the correct role.

We can then create a new Web Form:

Right-click on the previously created 'Admin' folder, expand 'Add' and select 'New Item...'.

Select to create a 'Web Form with Master Page' and name it 'ManageUserList.aspx'.

When prompted, select the 'Site.Master' stylesheet.

Set the Title property to "Manage Users" as shown below.

Following the same instructions as set out in the List Views tutorial, we will set up the content area with the relevant section blocks using Bootstrap conventions for 'container', 'row' and 'col-...' classes.

Below is the markup for the navigation buttons (Reset Password and Add New Record), and for the filter controls (textboxes).

A comment signposts where to insert your ListView control.

<h2><%: Title %>.</h2>

<section class="container">
  <section class="row">
    <!-- LIST VIEW column -->
    <section class="col-md-10">

      <section class="row">
        <section class="col-xs-8">
          <h3>User List</h3>
        </section>
        <section class="col-xs-4 text-right">
          <h3>
            <asp:linkbutton id="btnResetPassword" PostBackUrl="~/Admin/RegisterUser.aspx" CommandName="New" runat="server" CssClass="glyphicon glyphicon-wrench btn"/>
            <asp:linkbutton id="btnAddNew" PostBackUrl="~/Admin/RegisterUser.aspx" CommandName="New" runat="server" CssClass="glyphicon glyphicon-plus btn"/>
          </h3>
        </section>
      </section>

      <section class="row">
        <section class="col-md-12">

<!-- ***** INSERT LISTVIEW HERE ***** -->

        </section>
      </section>

    </section>

    <!-- SEARCH column -->
    <section class="col-md-2">

      <h3>Search</h3>
      <p><asp:TextBox ID="txtEmail" runat="server" placeholder="Email"></asp:TextBox></p>
      <p><asp:TextBox ID="txtPhoneNumber" runat="server" placeholder="Phone Number"></asp:TextBox></p>
      <p><asp:TextBox ID="txtUserName" runat="server" placeholder="Username"></asp:TextBox></p>
      <p><asp:TextBox ID="txtRoleID" runat="server" placeholder="Role ID"></asp:TextBox></p>
      <p><asp:TextBox ID="txtName" runat="server" placeholder="Role Name"></asp:TextBox></p>

      <p><asp:Button ID="btnSearchSite" runat="server" Text="Search" CssClass="btn btn-primary"/></p>

    </section>
  </section>
</section>

You can drag your ListView control from the toolbox to the relevant section.

In much the same way we have done during the List Views tutorial, we must establish an OnItemCommand to point to our code behind.

We will also create the Layout and Item templates, but we will not be needing an Insert template as we will instead use the current 'Register' page to create new users.

The mark-up is below.

(Note: the <%#Eval("[Roles]") %>' /> binding can be interchanged for two fields: RoleID and Name, if the GetUsersByParameters method is chosen to populate the data source.

          <asp:ListView ID="LvUserList" runat="server" 
            OnItemCommand="LvUserList_ItemCommand">
							
            <LayoutTemplate>
              <table runat="server" id="tblUserList" class="table table-hover">
                <tr runat="server" >
                  <th>User ID</th>
                  <th>Email</th>
                  <th>Phone Number</th>
                  <th>Two-Factor Enabled</th>
                  <th>Lockout End Date</th>
                  <th>Lockout Enabled</th>
                  <th>Access Failed Count</th>
                  <th>Username</th>

                  <th>Roles</th>
                </tr>
                <tr runat="server" id="itemPlaceholder" ></tr>
              </table>
            </LayoutTemplate>

            <ItemTemplate>
              <tr runat="server">
                <td><asp:Label ID="lblUserID" runat="server" Text='<%#Eval("[UserID]") %>' /></td>
                <td><asp:Label ID="lblEmail" runat="server" Text='<%#Eval("[Email]") %>' /></td>
                <td><asp:Label ID="lblPhoneNumber" runat="server" Text='<%#Eval("[PhoneNumber]") %>' /></td>
                <td><asp:Label ID="lblTwoFactorEnabled" runat="server" Text='<%#Eval("[TwoFactorEnabled]") %>' /></td>
                <td><asp:Label ID="lblLockoutEndDateUtc" runat="server" Text='<%#Eval("[LockoutEndDateUtc]") %>' /></td>
                <td><asp:Label ID="lblLockoutEnabled" runat="server" Text='<%#Eval("[LockoutEnabled]") %>' /></td>
                <td><asp:Label ID="lblAccessFailedCount" runat="server" Text='<%#Eval("[AccessFailedCount]") %>' /></td>
                <td><asp:Label ID="lblUserName" runat="server" Text='<%#Eval("[UserName]") %>' /></td>

                <td><asp:Label ID="lblRoles" runat="server" Text='<%#Eval("[Roles]") %>' /></td>
                <td><asp:LinkButton ID="btnView" runat="server" Text="View" CommandName="View" CommandArgument='<%#Eval("UserID") %>'/></td>
              </tr>
            </ItemTemplate>

          </asp:ListView>

Again, as with the List View tutorial, we will configure the code behind to accept textbox input into our variables, which in turn are used to pass values into the parameters of the Data Source.

Consider which function you would like to use. The code below shows the use of the 'GetUsersRolesByParameters' method, which concatenates roles names into a single field called Roles.

The image below shows the use of the 'GetUsersByParameters' method which instead would list users as many times as they have roles (but at least once if no role is found). With this last method your mark-up would have to Bind 'RoleID' and 'Name' in the ListView instead of 'Roles'.

protected void Page_Load(object sender, EventArgs e)
{
  string uEmail = txtEmail.Text.ToString();
  string uPhone = txtPhoneNumber.Text.ToString();
  string uUsername = txtUserName.Text.ToString();
  string rRoleID = txtRoleID.Text.ToString();
  string rName = txtName.Text.ToString();

  UsersAndRolesTableAdapter userRolesAdapter = new UsersAndRolesTableAdapter();
  LvUserList.DataSource = userRolesAdapter.GetUsersRolesByParameters(uEmail, uPhone, uUsername, rRoleID, rName);
  LvUserList.DataBind();
}

protected void LvUserList_ItemCommand(object sender, ListViewCommandEventArgs e)
{
  Response.Redirect("~/Admin/ManageUserView.aspx?id=" + e.CommandArgument.ToString());
}

You should now be familiar with creating new web forms, so create one for our Form View and call it 'ManageUserView.aspx'.

Change the title appropriately, such as "Manage User".

Populate with headings and section blocks, before dragging in your FormView control.

This will need to have an appropriate ID property and will include many of the properties talked through in the Form Views tutorial (except those relating to Insertion).

A full mark-up code of your Form View is shown below.

<h2><%: Title %>.</h2>

<section class="container">

  <asp:FormView ID="FvUserView" runat="server"
    CssClass="col-md-12"
    AllowPaging="false"
    DataKeyNames="UserID"
    OnItemCommand="FvUserView_ItemCommand"
    OnModeChanging="FvUserView_ModeChanging"
    OnItemUpdating="FvUserView_ItemUpdating"
    OnDataBound="FvUserView_DataBound">

    <ItemTemplate>
      <section class="row">
        <section class="col-xs-8">
           <h3>Form View (Read-Only)</h3>
        </section>
        <section class="col-xs-4 text-right">
          <h3>
            <asp:linkbutton id="btnInsertView" CommandName="New" runat="server" CssClass="glyphicon glyphicon-plus btn"/>
            <asp:linkbutton id="btnEditView" CommandName="Edit" runat="server" CssClass="glyphicon glyphicon-pencil btn"/>
            <asp:linkbutton id="btnListView" CommandName="ListView" runat="server" CssClass="glyphicon glyphicon-th-list btn"/>
          </h3>
        </section>
      </section>
      <section class="row">
        <section class="col-xs-12">
          <table>
            <tr><td>User ID:</td><td><asp:TextBox ID="txtUserID" runat="server" Enabled="False" Text='<%#Eval("[UserID]") %>'></asp:TextBox></td></tr>
            <tr><td>Email:</td><td><asp:TextBox ID="txtEmail" runat="server" Enabled="False" Text='<%#Eval("[Email]") %>'></asp:TextBox></td></tr>
            <tr><td>Phone Number:</td><td><asp:TextBox ID="txtPhoneNumber" runat="server" Enabled="False" Text='<%#Eval("[PhoneNumber]") %>'></asp:TextBox></td></tr>
            <tr><td>Two-Factor Enabled:</td><td><asp:TextBox ID="txtTwoFactorEnabled" runat="server" Enabled="False" Text='<%#Eval("[TwoFactorEnabled]") %>'></asp:TextBox></td></tr>
            <tr><td>Lockout End Date:</td><td><asp:TextBox ID="txtLockoutEndDateUtc" runat="server" Enabled="False" Text='<%#Eval("[LockoutEndDateUtc]") %>'></asp:TextBox></td></tr>
            <tr><td>Lockout Enabled:</td><td><asp:TextBox ID="txtLockoutEnabled" runat="server" Enabled="False" Text='<%#Eval("[LockoutEnabled]") %>'></asp:TextBox></td></tr>
            <tr><td>Access Failed Count:</td><td><asp:TextBox ID="txtAccessFailedCount" runat="server" Enabled="False" Text='<%#Eval("[AccessFailedCount]") %>'></asp:TextBox></td></tr>
            <tr><td>Username:</td><td><asp:TextBox ID="txtUserName" runat="server" Enabled="False" Text='<%#Eval("[UserName]") %>'></asp:TextBox></td></tr>
          </table>
        </section>
      </section>
    </ItemTemplate>

    <EditItemTemplate>
      <section class="row">
        <section class="col-xs-8">
          <h3>Form View (Edit)</h3>
        </section>
        <section class="col-xs-4 text-right">
          <h3>
            <asp:linkbutton id="btnInsertView" CommandName="New" runat="server" CssClass="glyphicon glyphicon-plus btn"/>
            <asp:linkbutton id="btnEditView" CommandName="Edit" runat="server" CssClass="glyphicon glyphicon-pencil btn disabled"/>
            <asp:linkbutton id="btnListView" CommandName="ListView" runat="server" CssClass="glyphicon glyphicon-th-list btn"/>
          </h3>
        </section>
      </section>
      <section class="row">
        <section class="col-xs-12">
          <table>
            <tr><td>User ID:</td><td><asp:TextBox ID="txtUserID" runat="server" Enabled="False" Text='<%#Eval("[UserID]") %>'></asp:TextBox></td></tr>
            <tr><td>Email:</td><td><asp:TextBox ID="txtEmail" runat="server" Enabled="True" Text='<%#Bind("[Email]") %>'></asp:TextBox></td></tr>
            <tr><td>Phone Number:</td><td><asp:TextBox ID="txtPhoneNumber" runat="server" Enabled="True" Text='<%#Bind("[PhoneNumber]") %>'></asp:TextBox></td></tr>
            <tr><td>Two-Factor Enabled:</td><td><asp:TextBox ID="txtTwoFactorEnabled" runat="server" Enabled="False" Text='<%#Eval("[TwoFactorEnabled]") %>'></asp:TextBox></td></tr>
            <tr><td>Lockout End Date:</td><td><asp:TextBox ID="txtLockoutEndDateUtc" runat="server" Enabled="False" Text='<%#Eval("[LockoutEndDateUtc]") %>'></asp:TextBox></td></tr>
            <tr><td>Lockout Enabled:</td><td><asp:TextBox ID="txtLockoutEnabled" runat="server" Enabled="False" Text='<%#Eval("[LockoutEnabled]") %>'></asp:TextBox></td></tr>
            <tr><td>Access Failed Count:</td><td><asp:TextBox ID="txtAccessFailedCount" runat="server" Enabled="False" Text='<%#Eval("[AccessFailedCount]") %>'></asp:TextBox></td></tr>
            <tr><td>Username:</td><td><asp:TextBox ID="txtUserName" runat="server" Enabled="True" Text='<%#Bind("[UserName]") %>'></asp:TextBox></td></tr>

            <tr><td></td><td><asp:LinkButton ID="btnUpdate" runat="server" CommandName="Update" Text="Save" CssClass="btn btn-primary"/></td></tr>
          </table>
        </section>
      </section>
    </EditItemTemplate>
			
    <EmptyDataTemplate>
      <section class="row">
        <section class="col-xs-12">
          <h3>(No record found.)</h3>
        </section>
      </section>
    </EmptyDataTemplate>

  </asp:FormView>

</section>

The Form View code behind is shown below. For more details of how it works refer to the Form Views tutorial.

protected void Page_Load(object sender, EventArgs e)
{
  if (!IsPostBack)
  {
    PageDataRefresh();
  }
}

private void PageDataRefresh()
{
  string uID = Request.QueryString["id"];
  if (uID == null || uID == "0")
  {
    Response.Redirect("~/Admin/RegisterUser.aspx");
  }

  /* *********** Configure DAL *********** */
  UsersAndRolesTableAdapter userAdapter = new UsersAndRolesTableAdapter();
  FvUserView.DataSource = userAdapter.GetUserByID(uID);
  FvUserView.DataBind();
}

protected void FvUserView_ItemCommand(object sender, FormViewCommandEventArgs e)
{
  switch (e.CommandName)
  {
    case "New":
      Response.Redirect("~/Admin/RegisterUser.aspx");
      break;
    case "Cancel":
      FvUserView.ChangeMode(FormViewMode.ReadOnly);
      PageDataRefresh();
      break;
    case "ListView":
      Response.Redirect("~/Admin/ManageUserList.aspx");
      break;
  }
}

protected void FvUserView_ModeChanging(object sender, FormViewModeEventArgs e)
{
  // Enable a FormView mode change (Read-Only, Edit/Update, New/Insert, Empty)
  FvUserView.ChangeMode((FormViewMode)e.NewMode);
  PageDataRefresh();
}

protected void FvUserView_ItemUpdating(object sender, FormViewUpdateEventArgs e)
{
  TextBox Email_txt = (TextBox)FvUserView.FindControl("txtEmail");
  TextBox PhoneNumber_txt = (TextBox)FvUserView.FindControl("txtPhoneNumber");
  TextBox UserName_txt = (TextBox)FvUserView.FindControl("txtUserName");

  string email = Email_txt.Text;
  string phoneNumber = PhoneNumber_txt.Text;
  string userName = UserName_txt.Text;

  UsersAndRolesTableAdapter userAdapter = new UsersAndRolesTableAdapter();

  try
  {
    string originalID = Request.QueryString["id"].ToString();

    // Conduct Update
    userAdapter.UpdateRecord(email, phoneNumber, userName, originalID);

    Response.Write("<script LANGUAGE='JavaScript' >alert('Record Edited')</script>");

    // Return to Read Only mode
    FvUserView.ChangeMode(FormViewMode.ReadOnly);
    PageDataRefresh();
  }
  catch (System.Exception ex)
  {
    Response.Write("<script LANGUAGE='JavaScript' >alert('An ERROR occurred connecting to the database.')</script>");
  }
}

protected void FvUserView_DataBound(object sender, EventArgs e)
{
  // DataItemCount checks how many records are returned
  // If no records are returned, redirect to the User List
  if (FvUserView.DataItemCount == 0)
  {
    Response.Redirect("~/Admin/ManageUserList.aspx");
  }
  else
  {
    FvUserView.ChangeMode(FormViewMode.Edit);
  }
}

If you are happy to have users register themselves on your site, then this is the end of the tutorial.

However, if you want to restrict the ability of creating new users to your administrators (or other roles) then read on.

Start by making a copy of the 'Register.aspx' web form (and child files such as the aspx.cs and aspx.designer.cs).

Paste this in our restricted 'Admin' folder. 

You can rename it appropriately, but we will need to replicate the name used in the code-behind. We have called ours 'RegisterUser.aspx'.

By expanding the new RegisterUser.aspx file in the Solution Explorer, open the RegisterUser.aspx.designer.cs file.

Note the namespace still points to '.Account' and the class is still called 'Register'.

There are also likely to be a few errors on the page that should resolve themselves after the next few steps.

Rename these, the first being the folder name: 'Admin', and the second the new filename (without extensions): 'RegisterUser'.

Next navigate to the RegisterUser.aspx.cs file.

A number of errors are also on this page, as are the namespace and class.

Make the same changes here, naming the namespace after the folder name: '.Admin', and the second based on the new filename without extensions: 'RegisterUser'.

Finally navigate to the markup script in RegisterUser.aspx.

A few items here. Firstly on the first row change the CodeBehind attributes link needs updating, and secondly it inherits the old '.Account.Register' page. 

The new link for CodeBehind should reflect the new name, e.g. 'RegisterUser.aspx.cs'.

The Inherit property should now end with the folder and web form (without extension), e.g. '.Admin.RegisterUser'.

Secondly we should adjust the mark-up for how this web form looks to make it fit our other forms. Locate and replace the row that states:

<h4>Create a new account</h4>

Your new script to replace this line is shown below.

We have included the line deleted inside <h3> tags this time. 

We have also included a link to the ManageUserList form.

<section class="container">
  <section class="row">
    <section class="col-xs-8">
      <h3>Create a new account</h3>
    </section>
    <section class="col-xs-4 text-right">
      <h3><asp:linkbutton id="btnListView" CommandName="ListView" PostBackUrl="~/Admin/ManageUserList.aspx" CausesValidation="false" runat="server" CssClass="glyphicon glyphicon-th-list btn"/></h3>
    </section>
  </section>
</section>

We should really delete the old 'Register.aspx' web form from the Account folder. Simply removing the navigation to it will still allow it to be accessible and create a security risk.

Right-click on it, and select 'Delete'.

We must now fix some of the hyperlinks to this location. Open the Login.aspx form in the Account folder.

Locate the <asp:Hyperlink> tag with the ID 'RegisterHyperlink' and delete it with its encasing paragraph <p> tags.

This deletion results in two errors in the code behind of Login.aspx. Locate and delete the code highlighted in the image below.

And finally we can update the header menu in the Site.Master file (located under the Solution itself).

Simply delete the list item linking to the Register page (shown below).

We will need to revisit this later to consider creating a password reset option for an administrator to action without needing to know the original password. An ideal way to accomplish this is to generate a random password unknown even to the administrator and have this emailed to the user, prompting them to change it on first login.

Well done.