User Administration Tool (Part 2)
In the
Part 1 of this series we started developing a web user control that allows
us to manage various aspects of user administration such as password recovery,
role mapping and profile management. We configured the database and web site for
availing membership, role and profile features of ASP.NET. Continuing our
development further we will now code various pieces of the functionality.
Binding the GridView with list of users
ASP.NET provides an inbuilt member called Membership through which membership
information can be retrieved. In order to retrieve a list of users and bind them
with the GridView we write a method called BindGrid(). There are two overloads
of this method. One accepting the search criteria and search by option and the
other without any parameters. Figure 1 shows these two overloads.
private void BindGrid(string criteria,FindByOptions option)
{
MembershipUserCollection m =null;
if (option == FindByOptions.Email)
{
ViewState["emailcriteria"] = criteria;
m = Membership.FindUsersByEmail(criteria);
}
else
{
ViewState["usernamecriteria"] = criteria;
m = Membership.FindUsersByName(criteria);
}
GridView1.DataSource = m;
GridView1.DataBind();
}
private void BindGrid()
{
if (ViewState["emailcriteria"] != null)
{
BindGrid(ViewState["emailcriteria"].ToString(),
FindByOptions.Email);
return;
}
if (ViewState["usernamecriteria"] != null)
{
BindGrid(ViewState["usernamecriteria"].ToString(),
FindByOptions.UserName);
return;
}
MembershipUserCollection m = Membership.GetAllUsers();
GridView1.DataSource = m;
GridView1.DataBind();
}
Figure 1: Binding
GridView with a list of users
The first overload takes two parameters. The search criteria is a string
containing the search pattern. The second parameter is of type FindByOptions.
FindByOptions is an enumeration defined by us and looks like this:
public enum FindByOptions
{
Email,UserName
}
Through the enumeration we specify whether we want to find users on the basis
of their email address or user name. Depending on this parameter we call
FindUsersByEmail() or FindUsersByName() method of Membership object. The return
value of FindUsersByEmail and FindUsersByName methods is a collection of type
MembershipUserCollection. Each member of this collection is of type
MembershipUser and represents a user. The MembershipUserCollection acts as a
data source for the GridView. Note that the code creates two ViewState variables
- emailcriteria and usernamecriteria - to store the corresponding search
criterions. This way we can filter the users across post backs. This overload of
the BindGrid() method is called when the administrator clicks on any of the Go
buttons of the Find Users panel.
In the second overload of the BindGrid() method the code checks for existence
of the same two ViewState variables. Their presence indicates that some search
filter is active. If any search filter is active then the previous overload of
the BindGrid() is called. Otherwise the code calls the GetAllUsers() method of
Membership object. The GetAllUsers() method returns all the users of the web
site in the form MembershipUserCollection. The collection is then bound with the
GridView as before. This overload of BindGrid() is called from the Load event of
the user control and also from various other places.
Figure 2 shows the Page_Load event handler of the user control.
protected void Page_Load(object sender,
EventArgs e)
{
CreateUserWizard1.ContinueDestinationPageUrl =
Request.Path;
if (!IsPostBack)
BindGrid();
}
Figure 2: Page_Load event handler of user control
The code sets the ContinueDestinationPageUrl property of the CreateUserWizard
control to the URL of the current web form. This way the administrator is
redirected to the same web form after clicking the Continue button of the
CreateUserWizard control. The code also calls the BindGrid() method to bind the
GridView with the list of users.
Handling Paging and Data Binding of the GridView
There might be many users registered with the web site and hence the GridView
must implement paging functionality. Since we are not using any Data Source
controls we need to implement paging functionality ourselves. In order to do so
we must handle two events related to paging � PageIndexChanging and
PageIndexChanged. The former is raised when a new page number is selected but
before navigating to the new page. The later is raised when the page index has
been changed. Figure 3 shows the event handlers for these two events.
protected void GridView1_PageIndexChanging
(object sender, GridViewPageEventArgs e)
{
GridView1.PageIndex = e.NewPageIndex;
BindGrid();
}
protected void GridView1_PageIndexChanged
(object sender, EventArgs e)
{
Label21.Text = "";
MultiView1.ActiveViewIndex = -1;}
Figure 3: Handling paging of the GridView
The PageIndexChanging event handler receives an event argument of type
GridViewPageEventArgs. The GridViewPageEventArgs class contains a property
called NewPageIndex that specifies the new page index. The code sets the
PageIndex property of the GridView to the value indicated in the NewPageIndex
property and calls the BindGrid() method.
In the PageIndexChanged event we clear off the MultiView by setting its
ActiveViewIndex property to -1. This way no View control will be visible and we
will be saved from the mismatch between currently displayed users and user
details already shown. For the same reason we also set the title label to empty
string.
When the administrator clicks on the Show button from a row we need to
display the details about that user. That means the Click event handler of the
Show button needs to know the UserName of the user that is shown by that row. In
order to satisfy this requirement we handle RowDataBound event of the GridView.
The RowDataBound event is raised for each and every row (including header and
footer row) when the row is data bound. Figure 4 shows the RowDataBound event
handler of the GridView.
protected void GridView1_RowDataBound
(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow &&
e.Row.RowState == DataControlRowState.Normal)
{
Button b1 = (Button)e.Row.FindControl("Button1");
Label l = (Label)e.Row.FindControl("Label1");
b1.CommandName = e.Row.RowIndex.ToString();
b1.CommandArgument = l.Text;
}
}
Figure 4: Handling RowDataBound event of the GridView.
We need to execute our code only for the rows that contain data i.e.
excluding header and footer rows and those that are in read only mode. This is
done with the help of RowType and RowState properties as shown. The code then
retrieves a reference to the Show button and user name label by calling
FindControl() method. Then the CommandName property of the button is set to the
current row index. Similarly the CommandArgument is set to the user name as
displayed in the label. This way the Show button�s Click event handler will know
which row has been clicked and what the corresponding user name is.
Editing and deleting users
Recollect that we have Edit, Delete, Update and Cancel buttons in the
ItemTemplate and EditItemTemplate of the GridView template column. We have set
the CommandName property of these buttons to Edit, Delete, Update and Cancel
respectively. Due to this these buttons will raise RowEditing, RowDeleting,
RowUpdating and RowCancelingEdit events when clicked. We will handle these
events in order to add editing capabilities to our GridView. Figure 5 shows the
event handlers for these events.
protected void GridView1_RowEditing
(object sender, GridViewEditEventArgs e)
{
GridView1.EditIndex = e.NewEditIndex;
BindGrid();
}
protected void GridView1_RowUpdating
(object sender, GridViewUpdateEventArgs e)
{
Label l=(Label) GridView1.Rows[e.RowIndex].
FindControl("Label1");
TextBox t1 = (TextBox)GridView1.Rows[e.RowIndex].
FindControl("TextBox12");
TextBox t2 = (TextBox)GridView1.Rows[e.RowIndex].
FindControl("TextBox13");
MembershipUser user = Membership.GetUser(l.Text);
user.Email = t1.Text;
user.Comment = t2.Text;
Membership.UpdateUser(user);
GridView1.EditIndex = -1;
BindGrid();
}
protected void GridView1_RowCancelingEdit
(object sender, GridViewCancelEditEventArgs e)
{
GridView1.EditIndex = -1;
BindGrid();
}
protected void GridView1_RowDeleting
(object sender, GridViewDeleteEventArgs e)
{
Label l = (Label)GridView1.Rows[e.RowIndex].
FindControl("Label1");
Membership.DeleteUser(l.Text);
BindGrid();
}
Figure 5: Editing and deleting related event handlers
The RowEditing event handler receives a parameter of type
GridViewEditEventArgs. The GridViewEditEventArgs class contains a property
called NewEditIndex that indicates the row index of the row whose Edit button
has been clicked. The code sets EditIndex property of the GridView to the value
of the NewEditIndex property. Setting the EditIndex property will cause the
GridView to enter in edit mode and EditItemTemplate will be displayed. The code
then calls the BindGrid() method to bind the GridView again.
The RowUpdating event is the important event wherein the actual operation of
updating the user is done. The RowUpdating event receives a parameter of type
GridViewUpdateEventArgs. The GridViewUpdateEventArgs class provides the row
index of the row being updated via RowIndex property. The code retrieves a
reference to the row being updated from the Rows collection of the GridView. The
code further obtains references to the user name label and email and comment
textboxes using FindControl() method. The MembershipUser object corresponding to
the specified user is obtained using the GetUser() method of the Membership
object. The code then updates the Email and Comment properties of the
MembershipUser instance and updates the user information back using UpdateUser()
method of the Membership object. Finally, EditIndex property of the GridView is
set to -1 to take the GridView back into read only mode.
The RowCancelingEdit event handler simply sets the EditIndex property of the
GridView to -1 and binds the GridView again. This will take the GridView into
read only mode.
The RowDeleting event handler receives a parameter of type
GridViewDeleteEventArgs. The GridViewDeleteEventArgs class supplies the RowIndex
of the row whose Delete button has been clicked. This RowIndex is used to
retrieve the row being deleted from Rows collection of the GridView. Once the
UserName is obtained from the user name label the code calls DeleteUser() method
of the Membership object and rebinds the GridView.
Showing the user information
Each row of the GridView displays a Show button in addition to Edit and
Delete buttons. The administrator can select the category of information to view
and click on the Show button. Figure 6 shows the outline of code that goes in
the Click event handler of the Show button.
protected void Button1_Click1
(object sender, EventArgs e)
{
Button b = (Button)sender;
MembershipUser user = Membership.
GetUser(b.CommandArgument.ToString());
ViewState["username"] = user.UserName;
Label21.Text = "Managing Details for " + user.UserName;
DropDownList ddl = (DropDownList)GridView1.
Rows[int.Parse(b.CommandName)].FindControl("DropDownList1");
switch (ddl.SelectedValue)
{
case "Status":
�
case "Activity":
�
case "Security":
�
case "Roles":
�
case "Profile":
�
}
}
Figure 6: Click event handler of Show button
The code first typecasts the sender parameter into a Button. This way we get
reference to the Show button that is being clicked. Recollect that we have set
the CommandArgument property of the Show button to the UserName. The code
retrieves a MembershipUser instance by calling GetUser() method of the
Membership object passing this CommandArgument as a parameter. The
MembershipUser instance is used by the remaining code to extract required
information about the user. The title label shows the UserName of the user whose
details are being shown. The UserName is persisted in a ViewState variabled
called username for later reference. Finally there is a switch statement that
tests the selection in the DropDownList control. Each case of the switch
statement populates and displays the corresponding View control from the
MultiView control. The code of each case is discussed next.
Showing user status
The User Status panel shows whether the user is active, locked our and on
line. The case for user status contains the code as shown in Figure 7.
...
case "Status":
CheckBox1.Checked = user.IsApproved;
CheckBox2.Checked = user.IsLockedOut;
CheckBox3.Checked = user.IsOnline;
if (user.IsLockedOut)
{
CheckBox2.Enabled = true;
}
else
{
CheckBox2.Enabled = false;
}
MultiView1.ActiveViewIndex = 0;
break;
...
Figure 7: Case for User Status
The code simply sets the checkbox values depending on the three Boolean
properties of MembershipUser instance � IsApproved, IsLockedOut and IsOnline.
The user might have registered with the web site but his account may not be
activated. This is indicated by the IsApproved property. By default when the
user performs five unsuccessful login attempts his account gets marked as
locked. This is indicated by IsLockedOut property. The administrator can unlock
a user only if his account is locked. Hence the lock out checkbox is enabled
only if the account is locked out. Similarly the IsOnLine property indicates
whether the user is currently logged in to the web site. The administrator can
not change the on line status of the user and hence the related checkbox is
always disabled. After assigning the Checked property the ActiveViewIndex
property of the MultiView is set to 0. This causes the MultiView to show the
User Status panel in the browser.
Showing user activity
The case for User Activity looks like Figure 8.
...
case "Activity":
Label11.Text = user.CreationDate.ToString();
Label12.Text = user.LastActivityDate.ToString();
Label13.Text = user.LastLockoutDate.ToString();
Label14.Text = user.LastLoginDate.ToString();
Label15.Text = user.LastPasswordChangedDate.ToString();
MultiView1.ActiveViewIndex = 1;
break;
...
Figure 8: Case for User Activity
The code in Figure 8 is fairly simple. It simply retrieves various activity
related properties and sets the labels accordingly. All the activity related
properties return DateTime instance. The CreationDate property returns the date
and time at which the user registered with the web site. The LastActivityDate
property indicates the date and time when the user was last authenticated. The
LastLockoutDate property returns the date and time when the user account was
last locked out. A user account can get locked out when the user tries to
signing in unsuccessfully for the number of attempts as indicated by
MaxInvalidPasswordAttempts property of the underlying provider. The
LastLoginDate property returns the date and time at which the user last logged
in to the web site. Finally LastPasswordChangedDate property returns the date
and time when the user last changed the password. The ActiveViewIndex property
of the MultiView control is set to 1 this time.
Managing security details
The case for Security is shown in Figure 9.
...
case "Security":
Label22.Text = user.PasswordQuestion;
if (!Membership.Provider.EnablePasswordReset)
{
Button5.Enabled = false;
}
if (!Membership.Provider.EnablePasswordRetrieval)
{
Button4.Enabled = false;
}
if (Membership.Provider.PasswordFormat ==
MembershipPasswordFormat.Hashed)
{
Button4.Enabled = false;
Button5.Enabled = false;
}
MultiView1.ActiveViewIndex = 2;
break;
...
Figure 9: Case for Security
The code displays the password question as entered by the user at the time of
registration. Then the code checks if the password reset feature is enabled by
the membership provider. This is done by checking the EnablePasswordReset
property. Depending on the outcome of the check the Reset Password button is
enabled or disabled. On the same lines the code checks if the password retrieval
feature is enabled by the membership provider. This is done by checking the
EnablePasswordRetrieval property. Additionally the code checks if the password
storage format is Hashed. Hashed passwords can not be retrieved even if
EnablePasswordRetrieval property returns true. Accordingly the Get Password
button is enabled or disabled. Finally ActiveViewIndex property of the MultiView
control is set to 2 so that the Security View is shown.
The Security panel allows the administrator to perform in all four operations
� changing the password, changing the security question and answer, retrieve
password and reset password. The Click event handler for Change Password button
is shown in Figure 10.
protected void Button6_Click
(object sender, EventArgs e)
{
MembershipUser user = Membership.
GetUser(ViewState["username"].ToString());
bool result = user.ChangePassword(
TextBox3.Text, TextBox4.Text);
}
Figure 10: Changing the password
The code retrieves the MembershipUser instance by calling GetUser() method of
the Membership object. Recollect that the Click event of the Show button stores
the user name in a ViewState variable called username. The same ViewState
variable is used here and passed to the GetUser() method. Finally ChangePassword()
method of the MembershipUser instance is called. The ChangePassword() method
accepts the old and new passwords and returns the true or false depending on
success or failure to change the password.
Figure 11 shows the Click event handler of the Change Password Q & A button.
protected void Button7_Click
(object sender, EventArgs e)
{
MembershipUser user = Membership.GetUser(
ViewState["username"].ToString());
bool result = user.ChangePasswordQuestionAndAnswer(
TextBox6.Text, TextBox7.Text, TextBox8.Text);
}
Figure 11: Changing the password question and answer.
The code retrieves the MembershipUser instance by calling GetUser() method of
the Membership object. Then ChangePasswordQuestionAndAnswer() method of the
MembershipUser instance is called. The ChangePasswordQuestionAndAnswer() method
accepts three parameters � password, new question and new answer. The method
returns true or false indicating the successor failure of the operation.
The Click event handler of the Get Password button is shown in Figure 12.
protected void Button4_Click
(object sender, EventArgs e)
{
MembershipUser user = Membership.GetUser(
ViewState["username"].ToString());
string str = user.GetPassword(TextBox9.Text);
Label30.Text = "Password :" + str;
}
Figure 12: Retrieving the password
The code retrieves the MembershipUser instance by calling GetUser() method of
the Membership object. In order to retrieve password of the user GetPassword()
method is called. The GetPassword() method accepts password answer as a
parameter and returns the password. The password is then displayed in a label.
Finally the Click event handler of Reset Password button is shown in Figure
13.
protected void Button5_Click
(object sender, EventArgs e)
{
MembershipUser user = Membership.GetUser(
ViewState["username"].ToString());
string str = user.ResetPassword(TextBox9.Text);
Label30.Text = "Password has been reset to :" + str;
}
Figure 13: Resetting the password
The code retrieves the MembershipUser instance by calling GetUser() method of
the Membership object as before. Then ResetPassword() method of the
MembershipUser instance is called. The method accepts the password answer as a
parameter and returns the new password as a return value. The new password is
then displayed in a label.
Summary
In this part we covered display of user information and password management.
The next part will deal with role management and managing profile of the user.