July 2008 Entries

OK, here is a nice chunk of code for a web part that will display the accounts within a Forms Based Authentication database. The code is complete all the bells and whistles including a context menu, paging, sorting, and searching. I couldn't get filtering to work so I opted for searching instead. It turns out searching will be a much more beneficial feature because selecting a username to filter on from a list of several hundred items would be pretty inefficient. This code should pretty much plug into a web part. I'm posting it here for reference - if someone needs clarification, post your question in the comments.

I apologize in advance for the line wrapping caused by the lack of width available in my blog. If you can't get a good copy and paste let me know and I'll put it somewhere as a text file.

using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using System.Data;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Web.Security;

namespace FBAUserManagement
{
    [Guid("55125528-2879-4a94-b2b2-aa45d807f54d")]
    public class FBAUserManagement : System.Web.UI.WebControls.WebParts.WebPart
    {
        DataSet oDataSet = new DataSet();
        DataTable dt = new DataTable();
        DataView oView = new DataView();
        SPGridView oGrid = new SPGridView();
        Table searchTable = new Table();
        TextBox txtUserSearch = new TextBox();
        Button btnSearch = new Button();

        public FBAUserManagement()
        {

        }

        protected override void CreateChildControls()
        {
            try
            {
                //Set up the search table appearing above the SPGridView
                searchTable.ID = "navTable";
                txtUserSearch.ID = "txtUserSearch";
                btnSearch.ID = "btnSearch";
                btnSearch.Text = "Search)";
                TableRow tr1 = new TableRow();
                searchTable.Rows.Add(tr1);
                TableCell tc1 = new TableCell();
                tc1.Text = "Search (e-mail):";
                TableCell tc2 = new TableCell();
                tc2.Controls.Add(txtUserSearch);
                TableCell tc3 = new TableCell();
                tc3.Controls.Add(btnSearch);
                tr1.Cells.Add(tc1);
                tr1.Cells.Add(tc2);
                tr1.Cells.Add(tc3);
                this.Controls.Add(searchTable);

                PopulateDataset();
                oView.Table = dt;
                oGrid.ID = "UserGrid";
                oGrid.DataSource = oView;
                oGrid.AutoGenerateColumns = false;
                oGrid.AllowSorting = true;
                oGrid.Sorting += new GridViewSortEventHandler(oGrid_Sorting);

                //Add the UserName column to the DataView
                SPMenuField colMenu = new SPMenuField();
                colMenu.HeaderText = "User Name";
                colMenu.TextFields = "UserName";
                colMenu.MenuTemplateId = "UserNameListMenu";
                colMenu.NavigateUrlFields = "UserName";
                colMenu.NavigateUrlFormat = "do.aspx?p={0}";
                colMenu.TokenNameAndValueFields = "NAME=UserName";
                colMenu.SortExpression = "UserName";

                //Build the Contenx Menu
                //Images used are all native to SharePoint
                MenuTemplate UserNameListMenu = new MenuTemplate();
                UserNameListMenu.ID = "UserNameListMenu";

                MenuItemTemplate userDisable = new MenuItemTemplate("Disable Account", "/_layouts/images/ServiceNotInstalled.gif");
                userDisable.ID = "userDisable";
                userDisable.ClientOnClickNavigateUrl = "do.aspx?this=%EDIT%that=%NAME%";
                UserNameListMenu.Controls.Add(userDisable);

                MenuItemTemplate userUnlock = new MenuItemTemplate("Unlock Account", "/_layouts/images/ServiceInstalled.gif");
                userUnlock.ID = "userUnlock";
                userUnlock.ClientOnClickNavigateUrl = "do.aspx?this=%EDIT%that=%NAME%";
                UserNameListMenu.Controls.Add(userUnlock);

                MenuItemTemplate userReset = new MenuItemTemplate("Reset Password", "/_layouts/images/recurrence.gif");
                userReset.ID = "userReset";
                userReset.ClientOnClickNavigateUrl = "do.aspx?this=%EDIT%that=%NAME%";
                UserNameListMenu.Controls.Add(userReset);

                this.Controls.Add(UserNameListMenu);
                oGrid.Columns.Add(colMenu);

                //Add E-mail Column
                BoundField emailLink = new BoundField();
                emailLink.DataField = "Email";
                emailLink.HeaderText = "E-mail";
                emailLink.SortExpression = "Email";
                oGrid.Columns.Add(emailLink);

                //Add Locked Out Column
                CheckBoxField isLockedOut = new CheckBoxField();
                isLockedOut.DataField = "IsLockedOut";
                isLockedOut.HeaderText = "Locked Out";
                isLockedOut.SortExpression = "IsLockedOut";
                oGrid.Columns.Add(isLockedOut);

                //Add Last Login Column
                BoundField LastLogin = new BoundField();
                LastLogin.DataField = "LastLoginDate";
                LastLogin.HeaderText = "Last Login";
                LastLogin.SortExpression = "LastLoginDate";
                oGrid.Columns.Add(LastLogin);

                //Add Last Password Change Column
                BoundField LastPasswordChange = new BoundField();
                LastPasswordChange.DataField = "LastPasswordChangedDate";
                LastPasswordChange.HeaderText = "Last Password Change";
                LastPasswordChange.SortExpression = "LastPasswordChangedDate";
                oGrid.Columns.Add(LastPasswordChange);

                this.Controls.Add(oGrid);

                //Turn on Paging and add event handler
                oGrid.PageSize = 10;
                oGrid.AllowPaging = true;
                oGrid.PageIndexChanging += new GridViewPageEventHandler(oGrid_PageIndexChanging);
                oGrid.PagerTemplate = null; // Must be called after Controls.Add(oGrid)

                //Recreate current sort if needed
                if (ViewState["SortDirection"] != null && ViewState["SortExpression"] != null)
                {
                    //We have sorting so it needs to be preserved
                    oView.Sort = ViewState["SortExpression"].ToString() + " " + ViewState["SortDirection"].ToString();
                }

                base.CreateChildControls();
            }
            catch (Exception e)
            {
                Page.Response.Write(e.ToString());
            }
        }

        void oGrid_PageIndexChanging(object sender, GridViewPageEventArgs e)
        {
            oGrid.PageIndex = e.NewPageIndex;
            oGrid.DataBind();
        }

        private void ApplyFilter()
        {
            if (txtUserSearch.Text != "")
            {
                oView.RowFilter = string.Format("Email LIKE '%{0}%'", txtUserSearch.Text);
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            //If there is a filter it must be applied before binding
            ApplyFilter();
            oGrid.DataBind();
            base.Render(writer);
        }

        private void oGrid_Sorting(object sender, GridViewSortEventArgs e)
        {
            //Sorting Logic
            string lastExpression = "";
            if (ViewState["SortExpression"] != null)
                lastExpression = ViewState["SortExpression"].ToString();

            string lastDirection = "asc";
            if (ViewState["SortDirection"] != null)
                lastDirection = ViewState["SortDirection"].ToString();

            string newDirection = "asc";
            if (e.SortExpression == lastExpression)
                newDirection = (lastDirection == "asc") ? "desc" : "asc";

            ViewState["SortExpression"] = e.SortExpression;
            ViewState["SortDirection"] = newDirection;

            oView.Sort = e.SortExpression + " " + newDirection;
            oGrid.DataBind();

        }

        private void PopulateDataset()
        {
            //Pull each user in the membership into a DataSet
            oDataSet.ExtendedProperties.Add("ID", "MyDataSet");
            dt = oDataSet.Tables.Add("Users");
            MembershipUserCollection muc;
            muc = Membership.GetAllUsers();
            int counter = 0;
            dt.Columns.Add("ID", Type.GetType("System.Int32"));
            dt.Columns.Add("UserName", Type.GetType("System.String"));
            dt.Columns.Add("Email", Type.GetType("System.String"));
            dt.Columns.Add("isLockedOut", Type.GetType("System.Boolean"));
            dt.Columns.Add("LastLoginDate", Type.GetType("System.DateTime"));
            dt.Columns.Add("LastPasswordChangedDate", Type.GetType("System.DateTime"));
            foreach (MembershipUser mu in muc)
            {
                DataRow dr;
                dr = dt.NewRow();
                dr["ID"] = counter;
                dr["UserName"] = mu.UserName;
                dr["Email"] = mu.Email;
                dr["isLockedOut"] = mu.IsLockedOut;
                dr["LastLoginDate"] = mu.LastLoginDate;
                dr["LastPasswordChangedDate"] = mu.LastPasswordChangedDate;
                dt.Rows.Add(dr);
                counter++;
            }

        }
    }
}

Here is an annoying inconsistency within the SharePoint Object Model. In list items, you can easily retrieve the Created Date and Modified Date through the OM. It's also pretty easy to get the created date and time for different versions of a list item. What threw me off was that the Created Date and Modified Date of the current item are stored in the time zone set in the SharePoint Administration Web Site (ex: GMT -7) however; the date and times for previous versions are stored in GMT. I was stuck on this probably for longer than I should have been but it's still a maddening irregularity. Anyway; here is how I dealt with the problem...

 

foreach (SPListItemVersion currentVersion in listItem.Versions)
{
        versionDateString = currentVersion.Created.ToString();
        versionDate = DateTime.Parse(versionDateString);
        versionDate = versionDate.AddHours(-7);
}