Bean Software Logo
ASP.NET Database Search Control
 Home
    Shop
    Advertise
    Write For Us
    Affiliate
    Newsletter
    Contact

Create Your Own Web Site Administration Tool in ASP.NET

Objective

The objective of this tutorial is to show the steps you have to follow to create your own WAT (Web site Administration tool) in ASP.NET.

 

Pre-requirements

In order to follow this tutorial, you have to meet the below conditions:

- IIS 5.0/6.0 web server installed on your PC;

- .NET 2.0 framework or above installed on your PC;

- An ASP.NET site (authentication is done via forms);

- A SQL server database (aspnetdb.mdf in our case) in the App_data folder of the application; This db must have the sql schema for membership, profiles and roles.

Introduction to WAT (Web site administration tool)

ASP.NET 2.0 already includes a WAT that is available from the Visual Studio 2005 Website menu via the ASP.NET Configuration menu option. This WAT allows only local websites to be administered. So, restrictions appear when you have your web site remotely hosting with a web hosting company.

The solution is to create your own WAT tool from the beginning as part of your web site. So, with this tool you can administrate the users and the roles in a web site.

Note, if you don't like to build this application from the beginning, you can download Web Site Administration Visual Studio Project, used in this tutorial

Membership management

The principal class of the ASP.NET 2.0 is System.Web.Security.Membership, which exposes a number of static methods to administrate the users from a web site. In order to have a complete description of these methods please consult the MSDN's documentation related to this issue.

The provider for the Membership system is configured in the web.config. Most of the default settings are hard-coded in the ASP.NET runtime due to improve performance. So, by configuring the provider in the web.config file, you override the defult setting with your own.

Please add the code below to your web.config file:

<connectionStrings>
  <remove name="LocalSqlServer"/>
  <add name="LocalSqlServer" connectionString="Data Source=.\SQLExpress;Integrated Security=True;User Instance=True;AttachDBFilename=|DataDirectory|aspnetdb.mdf" providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
  <!-- other settings... -->
  <membership defaultProvider="MyMembershipProvider"
      userIsOnlineTimeWindow="15">
    <providers>
      <add name="MyMembershipProvider"
         connectionStringName="LocalSqlServer"
         applicationName="/"
         enablePasswordRetrieval="true"
         enablePasswordReset="true"
         requiresQuestionAndAnswer="true"
         requiresUniqueEmail="true"
         passwordFormat="Clear"
         maxInvalidPasswordAttempts="5"
         passwordAttemptWindow="10"
         minRequiredPasswordLength="5"
         minRequiredNonalphanumericCharacters="0"
         type="System.Web.Security.SqlMembershipProvider, System.Web,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
          />
    </providers>
  </membership>
  <!-- other settings... -->
</system.web>

First of all, I configured in the <connectionStrings> section the connection string the application will use to connect to the db. In our case the name of the db is aspnetdb.mdf and is placed in the App_Code folder. As you can see here you can configure how many password attempts there will be, the password's length and other settings important for the Membership system.

Roles in ASP.NET

A system that supports authentication/authorization will have a support for roles too. The purpose of roles is to group users together for assigning a set of permissions, or authorizations. In this way the administrator's task is much easier.

Like in the Membership system, ASP.NET 2.0 has built-in support for roles, with regard to performance, security, and flexibility. There is a default provider for SQL Server, but if you want to customize it you can write your own provider.

The role management is disabled by default to improve performance for sites that don't need roles. In order to enable it, please enter the code below in the web.config file.

<system.web>
  <!-- other settings... -->
  <roleManager enabled="true" cacheRolesInCookie="true"
      cookieName="MYROLES" defaultProvider="MyRoleProvider">
    <providers>
      <add name="MyRoleProvider"
         connectionStringName="LocalSqlServer"
         applicationName="/"
         type="System.Web.Security.SqlRoleProvider, System.Web,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
          />
    </providers>
  </roleManager>
  <!-- other settings... -->
</system.web>

With this code you enabled the roles management system and configured your own provider.

System.Web.Security.Roles is the class that allows you to access and manage role information programmatically. In order to have a complete description of these methods please consult the MSDN's documentation related to this issue.

User profile

ASP.NET 2.0 comes with a built-in mechanism to manage user profiles, in an easy, yet very complete and flexible, way. The Profile module is easy to be implemented - for this you need to configure what the profile will contain. For every property, you have to define the property name and its type. So, if we want to add a BirthDate profile property, we must add the <add name="BirthDate" type="DateTime"/> code.

Please add the below code you your web.config file.

<system.web>
  <!-- other settings... -->
  <profile defaultProvider="MyProfileProvider">
    <providers>
      <add name="MyProfileProvider"
         connectionStringName="LocalSqlServer"
         applicationName="/"
         type="System.Web.Profile.SqlProfileProvider, System.Web,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
          />
    </providers>
    <properties>
      <add name="FirstName" type="String" />
      <add name="LastName" type="String" />
      <add name="Gender" type="String" />
      <add name="BirthDate" type="DateTime" />
      <add name="Occupation" type="String" />
      <add name="Website" type="String" />
      <group name="Address">
        <add name="Street" type="String" />
        <add name="PostalCode" type="String" />
        <add name="City" type="String" />
        <add name="State" type="String" />
      </group>
      <group name="Contacts">
        <add name="Phone" type="String" />
        <add name="Fax" type="String" />
      </group>
    </properties>
  </profile>
  <!-- other settings... -->
</system.web>

SQL Server data store

All data about users, their profiles and roles are stored in SQL Server data store.
ASP.NET 2.0 comes with a pre-built and ready to go data store structure for users, profiles and roles. It also provides default providers: SqlMembershipProvider, SqlProfileProvider and SqlRoleProvider.

As I already specified, there is a pre-build data store structure. So, you don't have to lose precious time creating tables, relations between tables and stored procedures. The membership system uses the next tables: aspnet_Applications, aspnet_Users and aspnet_Membership. For role management we have two more tables: aspnet_Roles and aspnet_UsersInRoles. For the profile system, tables are created after you specify the profile properties in the web.config file.

Create your own Website Administration Tool: The ManageUsers Administrative Page

Let's name this page ManageUsers.aspx. The user interface for this page can be divided into three parts:

The first part shows the number of registered users. It also shows how many of them are currently online.

The second part provides features for searching and listing the users. There is an "alphabet bar" with all the letters of the alphabet; when one is clicked, a grid is filled with all the users having names starting with that letter. There is also a facility to search for the users by providing a partial username or e-mail address.

In he third part with the help of a grid you can lists users and some of their profile properties.

The code below shows how you can implement the first two parts:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ManageUsers.aspx.cs" Inherits="ManageUsers" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form2" runat="server">
<div>Users Account Management</div>
<p></p>
<b>- The total registered users is: <asp:Literal runat="server" ID="lblTotalUsers" /><br />
- The total online users at this moment: <asp:Literal runat="server" ID="lblOnlineUsers" /></b>
<p></p>
In order to display all users whose name begins with letter click on the link letter:
<p></p>
<asp:Repeater runat="server" ID="rptAlphabetBar"
   OnItemCommand="rptAlphabetBar_ItemCommand">
   <ItemTemplate><asp:LinkButton ID="LinkButton1" runat="server" Text='<%# Container.DataItem %>'
      CommandArgument='<%# Container.DataItem %>' />&nbsp;&nbsp;
   </ItemTemplate>
</asp:Repeater>
<p></p>
Use the below feature to search users by partial username or e-mail:
<p></p>
<asp:DropDownList runat="server" ID="ddlUserSearchTypes">
   <asp:ListItem Text="UserName" Selected="true" />
   <asp:ListItem Text="E-mail" />
</asp:DropDownList>
contains
<asp:TextBox runat="server" ID="txtSearchText" />
<asp:Button runat="server" ID="btnSearch" Text="Search"
   OnClick="btnSearch_Click" />

For the alphabet bar I used a Repeater control, instead of a fixed list of links. To this Repeater control I bounded an array of characters that will finally be displayed as links. So, if later you want to remove certain characters from your alphabet bar, you will only have to remove those letters from that array.

The third part of the page contains a Gridview that will lists users and some of their properties. All we need is to specify the number and the type of columns for this grid. Below you will find the description for each column:

For the username, the creation date and last access date we will use BoundField columns. These values for these data will be displayed as strings.

For IsApproved property (read-only) use a CheckBoxField column.

For user's e-mail use a HyperLinkField to show the as an active link that uses the mailto: protocol.

To redirect the administrator to a page called EditUsers.aspx use another HyperLinkField column to show an edit image. This link will have the username as a querystring value and will allow the administrator to edit a user's profile.

To delete a user use a ButtonField column to create a graphical Delete button. To do this set the column's ButtonType property to "Image"

The code below shows how you should define this grid:

<asp:GridView ID="gvUsers" runat="server" AutoGenerateColumns="false"
DataKeyNames="UserName"
   OnRowCreated="gvUsers_RowCreated" OnRowDeleting="gvUsers_RowDeleting">
   <Columns>
      <asp:BoundField HeaderText="UserName" DataField="UserName" />
      <asp:HyperLinkField HeaderText="E-mail" DataTextField="Email"
         DataNavigateUrlFormatString="mailto:{0}" DataNavigateUrlFields="Email" />
      <asp:BoundField HeaderText="Created" DataField="CreationDate"
         DataFormatString="{0:MM/dd/yy h:mm tt}" />
      <asp:BoundField HeaderText="Last activity" DataField="LastActivityDate"
         DataFormatString="{0:MM/dd/yy h:mm tt}" />
      <asp:CheckBoxField HeaderText="Approved" DataField="IsApproved"
         HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center" />
      <asp:HyperLinkField Text="<img src='../images/edit.gif' border='0' />"
         DataNavigateUrlFormatString="EditUsers.aspx?UserName={0}"
         DataNavigateUrlFields="UserName" />
      <asp:ButtonField CommandName="Delete" ButtonType="Image"
         ImageUrl="~/images/delete.gif" />
   </Columns>
   <EmptyDataTemplate>No users found.</EmptyDataTemplate>
</asp:GridView>

If no users were found, you can customize your result with the <EmptyDataTemplate> section. This new feature was introduces only for ASP.NET 2.0.

In the page's code-behind file there is MemershipUserCollection object. This object is initialized with all the user information returned by Membership.GetAllUsers static method. In the Load event of this page we will use the Count property of this collection to display the total number of registered users and also the number of online users. I also created an array that contains letters that will be bound to the Repeater control in order to create the alphabet.

public partial class ManageUsers : Page
{
    private MembershipUserCollection allRegisteredUsers = Membership.GetAllUsers();
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.IsPostBack)
        {
            lblOnlineUsers.Text = Membership.GetNumberOfUsersOnline().ToString();
            lblTotalUsers.Text = allRegisteredUsers.Count.ToString();
            string[] alph = "A;B;C;D;E;F;G;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z;All".Split(';');
            rptAlphabetBar.DataSource = alph;
            rptAlphabetBar.DataBind();
        }
    }
    // other methods and event handlers go here...
}

When the administrator will click a letter link, the ItemCommand event of the Repeater control will raise. Here we will retrieve that letter and search for all users with the name that starts with that letter. When you click the All link, the gridview will show all the users. There are two search modes: SearchByEmail and SearchByText. Because in this case we will use a "SerachByText" search mode, the "SearchByEmail" mode is put to false. This search mode is stored in the Attributes collection of the gridview so that it is persisted in the view state, and doesn't get lost during a postback. Here's the code:

protected void rptAlphabetBar_ItemCommand(object source, RepeaterCommandEventArgs e)
{
    gvUsers.Attributes.Add("SearchByEmail", false.ToString());
    if (e.CommandArgument.ToString().Length == 1)
    {
        gvUsers.Attributes.Add("SearchText", e.CommandArgument.ToString() + "%");
        this.BindAllUsers(false);
    }
    else
    {
        gvUsers.Attributes.Add("SearchText", "");
        this.BindAllUsers(false);
    }
}

In this event the BindAllUsers method is called. The Boolean value that is passed as an input parameter indicates if the allRegisteredUsers collection must be repopulated (especially when a user is deleted). The text to search for and the search mode are retrieved from the grid's Attributes collection. Below is the code:

private void BindAllUsers(bool reloadAllUsers)
{
    MembershipUserCollection allUsers = null;
    if (reloadAllUsers)
        allUsers = Membership.GetAllUsers();
    string searchText = "";
    if (!string.IsNullOrEmpty(gvUsers.Attributes["SearchText"]))
        searchText = gvUsers.Attributes["SearchText"];
    bool searchByEmail = false;
    if (!string.IsNullOrEmpty(gvUsers.Attributes["SearchByEmail"]))
        searchByEmail = bool.Parse(gvUsers.Attributes["SearchByEmail"]);
    if (searchText.Length > 0)
    {
        if (searchByEmail)
            allUsers = Membership.FindUsersByEmail(searchText);
        else
            allUsers = Membership.FindUsersByName(searchText);
    }
    else
    {
        allUsers = allRegisteredUsers;
    }
    gvUsers.DataSource = allUsers;
    gvUsers.DataBind();
}

This BindAllUsers method is also called when the Search button is clicked. In this case, the search mode will be set according to the value selected in the ddlUserSearchTypes dropdown list control. If you choose the "SearchText" search mode, to the entered search string will be added at the beginning and at the end the "%" character, so LIKE query will be performed:

protected void btnSearch_Click(object sender, EventArgs e)
{
    bool searchByEmail = (ddlUserSearchTypes.SelectedValue == "E-mail");
    gvUsers.Attributes.Add("SearchText", "%" + txtSearchText.Text + "%");
    gvUsers.Attributes.Add("SearchByEmail", searchByEmail.ToString());
    BindAllUsers(false);
}

If you want to delete a user you have to click the trashcan icon. For this the GridView raises the RowDeleting event because the column's CommandName property is set to Delete. Inside this event handler you will use the static methods of the Membership and ProfileManager classes to delete the user account and its accompanying profile. BindAllUser method is called (with true as a parameter), so that the collection of all users is refreshed, and the new information is displayed:

protected void gvUsers_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    string userName = gvUsers.DataKeys[e.RowIndex].Value.ToString();
    ProfileManager.DeleteProfile(userName);
    Membership.DeleteUser(userName);
    BindAllUsers(true);
    lblTotalUsers.Text = allRegisteredUsers.Count.ToString();
}

If you delete a user you cannot undo this action. This is why you should have the administrator confirm this action before proceeding. Through the button's OnClientClick property you can add a "confirm" JavaScript in the link's onclick client-side event. All these can be done in the gridview's RowCreated event to get a reference to each link as soon as its parent row and all its contents are created. Here's the code:

protected void gvUsers_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        ImageButton btn = e.Row.Cells[6].Controls[0] as ImageButton;
        btn.OnClientClick = "if (confirm('Are you sure you want to delete this user?') == false) return false;";
    }
}

This script is added only for rows of type DataRow. In this way we will avoid to add this script to the header, footer, and pagination bars.

The UserProfile user control

After registering every user has to fill in some forms with his profile. Due to flexibility I grouped all these forms and controls into a user control. In this way I can use the same user control in the administration section to edit the profile for a user.

So, the profile proprieties that we configured in web.config file will receive the values from these forms. Please see below the mark-up code for this user control.

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="UserProfile.ascx.cs"
Inherits="UserProfile" %>
 
<table cellpadding="2">
   <tr>
      <td width="130">First name:</td>
      <td width="300">
         <asp:TextBox ID="txtFirstName" runat="server" Width="99%"></asp:TextBox>
      </td>
   </tr>
   <tr>
      <td >Last name:</td>
      <td>
         <asp:TextBox ID="txtLastName" runat="server" Width="99%" />
      </td>
   </tr>
   <tr>
      <td >Gender:</td>
      <td>
         <asp:DropDownList runat="server" ID="ddlGenders">
            <asp:ListItem Text="Please select one..." Value="" Selected="True" />
            <asp:ListItem Text="Male" Value="M" />
            <asp:ListItem Text="Female" Value="F" />
         </asp:DropDownList>
      </td>
   </tr>
   <tr>
      <td>Birth date:</td>
      <td>
         <asp:TextBox ID="txtBirthDate" runat="server" Width="99%"></asp:TextBox>
         <asp:CompareValidator runat="server" ID="valBirthDateFormat"
            ControlToValidate="txtBirthDate"
            SetFocusOnError="true" Display="Dynamic" Operator="DataTypeCheck"
            Type="Date" ErrorMessage="The format of the birth date is not valid."
            ValidationGroup="EditProfile">
            <br />The format of the birth date is not valid.
         </asp:CompareValidator>
      </td>
   </tr>
   <tr>
      <td>Occupation:</td>
      <td>
         <asp:DropDownList ID="ddlOccupations" runat="server" Width="99%">
            <asp:ListItem Text="Please select one..." Value="" Selected="True" />
            <asp:ListItem Text="Academic" />
            <asp:ListItem Text="Accountant" />
            <asp:ListItem Text="Actor" />
            <asp:ListItem Text="Architect" />
            <asp:ListItem Text="Artist" />
            <asp:ListItem Text="Business Manager" />
            <%-- other options... --%>
            <asp:ListItem Text="Other" />
         </asp:DropDownList>
      </td>
   </tr>
   <tr>
      <td>Website:</td>
      <td>
         <asp:TextBox ID="txtWebsite" runat="server" Width="99%" />
      </td>
   </tr>
</table>
<p></p>
<div >Address</div>
<p></p>
<table cellpadding="2">
   <tr>
      <td width="130" class="fieldname">Street:</td>
      <td width="300">
         <asp:TextBox runat="server" ID="txtStreet" Width="99%" />
      </td>
   </tr>
   <tr>
      <td>City:</td>
      <td><asp:TextBox runat="server" ID="txtCity" Width="99%" /></td>
   </tr>
   <tr>
      <td>Zip / Postal code:</td>
      <td><asp:TextBox runat="server" ID="txtPostalCode" Width="99%" /></td>
   </tr>
   <tr>
      <td>State / Region:</td>
      <td><asp:TextBox runat="server" ID="txtState" Width="99%" /></td>
   </tr>  
</table>
<p></p>
<div >Other contacts</div>
<p></p>
<table cellpadding="2">
   <tr>
      <td width="130">Phone:</td>
      <td width="300"><asp:TextBox runat="server" ID="txtPhone" Width="99%" /></td>
   </tr>
   <tr>
      <td>Fax:</td>
      <td><asp:TextBox runat="server" ID="txtFax" Width="99%" /></td>
   </tr>
</table>

In the user control's load event the first thing we have to do is to retrieve the profile for the user and to update the forms with these details.

protected void Page_Load(object sender, EventArgs e)
{
    if (!this.IsPostBack)
    {
        // if the UserName property contains an emtpy string, retrieve the profile
        // for the current user, otherwise for the specified user
        ProfileCommon profile = this.Profile;
        if (this.UserName.Length > 0)
            profile = this.Profile.GetProfile(this.UserName);
        txtFirstName.Text = profile.FirstName;
        txtLastName.Text = profile.LastName;
        ddlGenders.SelectedValue = profile.Gender;
        if (profile.BirthDate != DateTime.MinValue)
            txtBirthDate.Text = profile.BirthDate.ToShortDateString();
        ddlOccupations.SelectedValue = profile.Occupation;
        txtWebsite.Text = profile.Website;
        txtStreet.Text = profile.Address.Street;
        txtCity.Text = profile.Address.City;
        txtPostalCode.Text = profile.Address.PostalCode;
        txtState.Text = profile.Address.State;
        txtPhone.Text = profile.Contacts.Phone;
        txtFax.Text = profile.Contacts.Fax;
    }
}

The most important method is the Save method. It has the purpose to save in the user's profile the values from the forms.

public void Save()
{
    // if the UserName property contains an emtpy string, save the current user's
    // profile, othwerwise save the profile for the specified user
    ProfileCommon profile = this.Profile;
    if (this.UserName.Length > 0)
        profile = this.Profile.GetProfile(this.UserName);
    profile.FirstName = txtFirstName.Text;
    profile.LastName = txtLastName.Text;
    profile.Gender = ddlGenders.SelectedValue;
    if (txtBirthDate.Text.Trim().Length > 0)
        profile.BirthDate = DateTime.Parse(txtBirthDate.Text);
    profile.Occupation = ddlOccupations.SelectedValue;
    profile.Website = txtWebsite.Text;
    profile.Address.Street = txtStreet.Text;
    profile.Address.City = txtCity.Text;
    profile.Address.PostalCode = txtPostalCode.Text;
    profile.Address.State = txtState.Text;
    profile.Contacts.Phone = txtPhone.Text;
    profile.Contacts.Fax = txtFax.Text;
    profile.Save();
}

It is important that this user control to benefit of the ViewState facility, but we might have problems if we place this user control in a page that has the ViewState disabled. To resolve this problem we have to use the ControlState facility. It does mainly the same thing like Viewstate, but the difference is that it will be always enabled no matter of the ViewState from the web page. To implement the ControlState facility we have to write the code below:

private string _userName = "";
public string UserName
{
    get { return _userName; }
    set { _userName = value; }
}
protected void Page_Init(object sender, EventArgs e)
{
    this.Page.RegisterRequiresControlState(this);
}
protected override void LoadControlState(object savedState)
{
    object[] ctlState = (object[])savedState;
    base.LoadControlState(ctlState[0]);
    _userName = (string)ctlState[1];
}
protected override object SaveControlState()
{
    object[] ctlState = new object[2];
    ctlState[0] = base.SaveControlState();
    ctlState[1] = _userName;
    return ctlState;
}

The EditUsers Administrative Page

You can access the EditUsers.aspx page clicking the edit image for a user in the ManageUsers.aspx grid. The username of that user is passed as a querystring value. This page allows an administrator to see all the membership details about that user and to edit the user's personal profile. The user interface of the page is simple and is divided in three parts:

The first part displays the details from MembershipUser. All controls are read-only, except for those that are bound to the IsApproved and IsLockedOut properties. Through the IsLockedOut property you can unlock a user account.

The second part contains a CheckBoxList that displays all the roles created for this application. Here you can add and remove users to and from roles. There is also a TextBox control and a button to create a new role.

In the third part you can edit a user's profile with the help of the user control we have created.

Below you can find the mark-up code for the EditUsers.aspx web page:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="EditUsers.aspx.cs" Inherits="EditUsers" %>
<%@ Register Src="UserProfile.ascx" TagName="UserProfile" TagPrefix="mb" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>General user information</div>
<p></p>
<table cellpadding="2">
   <tr>
      <td width="130">UserName:</td>
      <td width="300"><asp:Literal runat="server" ID="lblUserName" /></td>
   </tr>
   <tr>
      <td>E-mail address:</td>
      <td><asp:HyperLink ID="lnkEmailAddress" runat="server" /></td>
   </tr>
   <tr>
      <td>Registered:</td>
      <td><asp:Literal  ID="lblRegistered" runat="server" /></td>
   </tr>
   <tr>
      <td>Last Login:</td>
      <td><asp:Literal ID="lblLastLogin" runat="server" /></td>
   </tr>
   <tr>
      <td>Last Activity:</td>
      <td><asp:Literal ID="lblLastActivity" runat="server" /></td>
   </tr>
   <tr>
      <td>Online Now:</td>
      <td><asp:CheckBox ID="chkIsOnlineNow" Enabled="false" runat="server" /></td>
   </tr>
   <tr>
      <td>Approved:</td>
      <td><asp:CheckBox ID="chkIsApproved" AutoPostBack="true" OnCheckedChanged="chkIsApproved_CheckedChanged" runat="server"/></td>
   </tr>
   <tr>
      <td>Locked Out:</td>
      <td><asp:CheckBox ID="chkIsLockedOut" AutoPostBack="true" OnCheckedChanged="chkIsLockedOut_CheckedChanged" runat="server"/></td>
   </tr>
</table>
<p></p>
<p></p>
<div>User's roles</div>
<p></p>
<asp:CheckBoxList runat="server" ID="chklRoles" RepeatColumns="5" CellSpacing="4" />
<table cellpadding="2" width="450">
   <tr><td align="right">
      <asp:Label runat="server" ID="lblRolesOK"
         Text="Roles were successfully updated" Visible="false" />
      <asp:Button ID="btnUpdate" Text="Update"
         OnClick="btnUpdate_Click" runat="server"/>
   </td></tr>
   <tr><td align="right">
      <small>Create new role: </small>
      <asp:TextBox ID="txtNewRole" runat="server"/>
      <asp:RequiredFieldValidator ID="validatorRequireRole"
         ControlToValidate="txtNewRole" SetFocusOnError="true"
         ErrorMessage="Role name is required."   ValidationGroup="CreateRole" runat="server">
      </asp:RequiredFieldValidator>
      <asp:Button runat="server" ID="btnCreate" Text="Create"
         ValidationGroup="CreateRole" OnClick="btnCreate_Click" />
   </td></tr>
</table>
<p></p>
<div>Edit user's profile</div>
<p></p>
<mb:UserProfile ID="UserProfile1" runat="server" />
   <table cellpadding="2" width="400">
   <tr><td align="right">
      <asp:Label ID="lblProfileOK" Text="Profile was successfully updated" Visible="false" runat="server"/>
      <asp:Button ID="btnUpdateProfile" Text="Update" runat="server"
       ValidationGroup="EditProfile" OnClick="btnUpdateProfile_Click"/>
   </td></tr>
</table>
</form>
</body>
</html>

In the Page_Load event handler the username is retrieved from the querystring and a MembershipUser instance is created for that user. Now, all profile proprieties are shown in the first part of the page:

public partial class EditUsers : Page
{
    string userName = "";
    protected void Page_Load(object sender, EventArgs e)
    {
        // retrieve the username from the querystring
        userName = this.Request.QueryString["UserName"];
 
        lblRolesOK.Visible = false;
        lblProfileOK.Visible = false;
 
        if (!this.IsPostBack)
        {
            UserProfile1.UserName = userName;
            MembershipUser user = Membership.GetUser(userName);
            lblUserName.Text = user.UserName;
            lnkEmailAddress.Text = user.Email;
            lnkEmailAddress.NavigateUrl = "mailto:" + user.Email;
            lblRegistered.Text = user.CreationDate.ToString("f");
            lblLastLogin.Text = user.LastLoginDate.ToString("f");
            lblLastActivity.Text = user.LastActivityDate.ToString("f");
            chkIsLockedOut.Checked = user.IsLockedOut;
            chkIsLockedOut.Enabled = user.IsLockedOut;
            chkIsOnlineNow.Checked = user.IsOnline;
            chkIsApproved.Checked = user.IsApproved;
            BindRoles();
        }
    }
}

The purpose of the BindRoles method is to fill the CheckBoxList with all the available roles and to check the ones the user belongs to:

private void BindRoles()
{
    chklRoles.DataSource = Roles.GetAllRoles();
    chklRoles.DataBind();
    foreach (string role in Roles.GetRolesForUser(userName))
        chklRoles.Items.FindByText(role).Selected = true;
}

When the Update Roles button is pressed, the first thing to do is to remove the user from all his roles and after that to add him to the selected ones. We have to remove the users from all the roles because we will receive an exception if we try to add a user in a role he is already a member of. Please see the below code:

protected void btnUpdate_Click(object sender, EventArgs e)
{
    // first remove the user from all roles...
    string[] currentRoles = Roles.GetRolesForUser(userName);
 
    if (currentRoles.Length > 0)
        Roles.RemoveUserFromRoles(userName, currentRoles);
    // and then add the user to the selected roles
    List<string> newRoles = new List<string>();
    foreach (ListItem item in chklRoles.Items)
    {
        if (item.Selected)
            newRoles.Add(item.Text);
    }
    Roles.AddUserToRoles(userName, newRoles.ToArray());
 
    lblRolesOK.Visible = true;
}

As you see, we used Roles.AddUserToRoles method instead of the Roles.AddUserToRole method. For this we created first a list with all the new roles the user will belong.

Below you can find the code for creating a new role. First we check to see if there is a role with the same name. If there isn't, we can create it. The BindRoles method is called to refresh the list of available roles:

protected void btnCreate_Click(object sender, EventArgs e)
{
    if (!Roles.RoleExists(txtNewRole.Text.Trim()))
    {
        Roles.CreateRole(txtNewRole.Text.Trim());
        BindRoles();
    }
}

When the Approved checkbox is clicked, the event handler updates the MembershipUser object's IsApproved property according to the checkbox's value, and then save the change:

protected void chkIsApproved_CheckedChanged(object sender, EventArgs e)
{
    MembershipUser user = Membership.GetUser(userName);
    user.IsApproved = chkIsApproved.Checked;
    Membership.UpdateUser(user);
}

You can unlock a user in the same way. For this we will use the UnlockUser method of the MembershipUser object. After that, the checkbox is made read-only because you can't lock out a user. Please see the below code:

protected void chkIsLockedOut_CheckedChanged(object sender, EventArgs e)
{
    if (!chkIsLockedOut.Checked)
    {
        MembershipUser user = Membership.GetUser(userName);
        user.UnlockUser();
        chkIsLockedOut.Enabled = false;
    }
}

When the profile box's Update button is clicked, a call to the UserProfile's Save method is made and the user’s profile is updated:

protected void btnUpdateProfile_Click(object sender, EventArgs e)
{
    UserProfile1.Save();
    lblProfileOK.Visible = true;
}

This tutorial is written by Adrian Tarjoianu.


Tutorial toolbar:  Tell A Friend  |  Add to favorites  |  Feedback  |   


comments powered by Disqus

Related articles:

1. Built-In Asynchronous I/O Support in ASP.NET
2. Tracking Your Website's Visitors and Statistic in ASP.NET