Free SharePoint Redirection WebPart Programming SharePoint WebParts With LINQ to Active Directory Part 2

In the first part of this series, I illustrated the initial building of the primary WebPart class and the workhorse method that taps into Active Directory using LINQ to Active Directory to retrieve the current user’s department. Now, we have to use the returned department string to query down into a helper redirection SharePoint list and use a LINQ query to compare the two strings. We can grab the corresponding value out of the URL column, and they using standard redirection techniques push the user to the destination.

The PushUserToDivisionSite method is responsible for the actual redirection of the user. Firstly, a new SPSite & SPWeb object will be instantiated using the Url property, then a SPList constructed using the List property. To build the Where construct a SPList.Items.Cast<SPListItem>().Where is used performing an Equals on the department of the user and the value of the Title column. This should actually be a String.Compare so that you could bypass any case sensitivity problems, but I am just noticing that now. So you might want to fix that. An Enumerable.FirstOrDefault is used to return the desired SPListItem, and the URL value brought out. This value is then used in a trivial Page.Response.Redirect.

[csharp]

private void PushUserToDivisionSite()
{
using (var site = new SPSite(Url))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists[List];
var results = list.Items.Cast().Where(item => Equals(item[“Title”].ToString(), GetCurrentUserDepartment()));
if (results.Count() > 0)
{
SPListItem hydratedItem = results.FirstOrDefault();
string url = hydratedItem[UrlProperty].ToString();
if (!string.IsNullOrEmpty(url))
{
Page.Response.Redirect(url, true);
}
else
{
HandleException(new Exception(“Redirection Did Not Occur. Please Check The URL Property Of The List Being Used.”));
}
}
}
}
}

[/csharp]

The CreateChildControls method of the WebPart doesn’t do much besides call relevant helper methods. Since the GetCurrentUserDepartment method will either return the department or a constant string value as specified in the global fields, that can simplify be leveraged in a switch. So, either the department is returned, or the constant matches and a new Exception is thrown using the HandleException method which doesn’t do much but emit a literal control.

[csharp]

protected override void CreateChildControls()
{
if (!_error)
{

try
{
base.CreateChildControls();
string department = GetCurrentUserDepartment();
switch (department)
{
case _breakStatement:
HandleException(new Exception(“It Appears Your Active Directory Account Has No Department Set!”));
break;
default:
PushUserToDivisionSite();
break;
}
}
catch (Exception ex)
{
HandleException(ex);
}
}
}

[/csharp]

And that’s it. So going through this again, I think there are two big things that LINQ to Active Directory provides that are crucial to recognize, regardless of whether you like the actual implementation of the concept.

  1. Providing a layer of abstraction over Active Directory functions promotes standardization and ease of use.
  2. Being able to use LINQ querying constructs is extremely helpful when trying to keep code succinct and providing homogenous querying mechanisms.
Share

Free SharePoint Redirection WebPart – Programming SharePoint WebParts With LINQ to Active Directory – Part 1

The real power of LINQ lies in the ambiguity to query collections of all Types, as long as they implement the IQueryable interface. Since Active Directory is essentially a collection of users, one should be able to use a LINQ-esque fashion to manipulate objects out of that particular store as well.

This is where LINQ to AD is awesome! It allows a wrapper to exist around System.DirectoryServices to allow more contemporary, succinct language constructs to be used while developing AD DS integrated tooling. Since I do a lot of security software that exploits AD DS, I wanted to give it a shot!

And it’s pretty impressive. As you can see in the code being explained, there are some quirks, but it’s a pretty cool approach that I believe I am going to use more frequently because I enjoy the layer of abstraction and really, really enjoy being able to use the LINQ code approach.

I have to break this up into two posts because it is exhaustively long. The second post has the download link.

So, for a test project let’s go through building a Redirection WebPart that will satisfy a couple requirements:

  1. Get a reference to the current user
  2. Query into AD DS, find that user, and retrieve the department property
  3. Using the department property, query into a pre-existing SPList, match it, and retrieve a corresponding URL
  4. Redirect the current context to that returned URL

First things first, you have to do download the LINQ to Active Directory project.

Firstly, the appropriate references have to be made, notably to the LINQ to Active Directory project as a referenced project or a compiled assembly. As well, the Active DS Type Library must be referenced so that the IADs interface is exposed. You will see why this is important shortly, but needless to say if not referenced you will get a somewhat cryptic error about an “unspecified error” occurring which is a pain in the ass.

This is what my project structure looks like following:

10-27-2009 1-43-04 PM

As you can see, there are two references out of some pretty orthodox ones being made. The Bds.DirectoryServices.Linq project is referenced, along with the ActiveDs assembly like discussed previously.

The WebPart consists of only two classes. The first one, User, is meant to structure a User object with the properties that should be queried during the AD iterations. For my particular requirement I am only interested in getting the account name and the corresponding department entry. The class and property decorations are provided to specify targets,  DirectoryAttribute for example correlates to the underlying LDAP property to query.

[csharp]

[DirectorySchema(“user”, typeof(IADsUser))]
public class User
{
[DirectoryAttribute(“sAMAccountName”)]
public string AccountName { get; set; }

[DirectoryAttribute(“Department”, DirectoryAttributeType.ActiveDs)]
public string Department { get; set; }
}

[/csharp] 

I am going to throw on some generic properties now into the WebPart class to keep some of the more mutable values dynamic. Just four of them, one a URL string to hyrdate a SPSite object where the SPList exists, a string to represent which SPList to get, a third string to allow the column name to be specified, and a fourth string for the AD DS domain name.

[csharp]

        [Personalizable, WebBrowsable(true),
         WebDescription(“The Url Where The Redirection List Exists”),
         Category(“Global Mode Configuration”),
         WebPartStorage(Storage.Shared),
         DefaultValue(“”),
         WebDisplayName(“URL Of Redirection List Site”)]
        public string Url { get; set; }

        [Personalizable, WebBrowsable(true),
         WebDescription(“Redirection List”),
         Category(“Global Mode Configuration”),
         WebPartStorage(Storage.Shared),
         DefaultValue(“”),
         WebDisplayName(“Redirection List”)]
        public string List { get; set; }

        [Personalizable, WebBrowsable(true),
         WebDescription(“URL List Property”),
         Category(“Global Mode Configuration”),
         WebPartStorage(Storage.Shared),
         DefaultValue(“”),
         WebDisplayName(“Field Name That Contains URL Values”)]
        public string UrlProperty { get; set; }

        [Personalizable, WebBrowsable(true),
         WebDescription(“Active Directory Domain Name To Use”),
         Category(“Global Mode Configuration”),
         WebPartStorage(Storage.Shared),
         DefaultValue(“”),
         WebDisplayName(“AD DS Domain name”)]
        public string DomainName { get; set; }

[/csharp]

Getting the users department from AD using LINQ to Active Directory is a breeze as illustrated in the GetCurrentUserDepartment method below. The _entry global field is a DirectoryEntry object which is intialized OnLoad to the domain string literal specified as a WebPart property (the DomainName auto-property), using some brief string massaging to make it an agreeable DirectoryEntry constructor parameter format-wise.

[csharp]
protected override void OnLoad(EventArgs e)
{
if (!_error)
{
try
{
base.OnLoad(e);
if (!string.IsNullOrEmpty(DomainName))
{
_entry = new DirectoryEntry(string.Format(“LDAP://{0}”, DomainName));
}
else
{
HandleException(new Exception(“Please Set A Domain Name!”));
}
}
catch (Exception ex)
{
HandleException(ex);
}
}
}

[/csharp]

The second SearchScope parameter will specify the tranversal rules for the query, since I want to recur the whole substree I am going to use SearchScope.Subtree.

Following, the current SPContext is going to be used to get a the current SPUser.Login name, which passed into a regular expression to clean it as to match the account name return from Active Directory. An IQueryable<User> collection is built by executing a LINQ Where clause against the previously created DirectorySource object, matching the current SharePoint user to their record in Active Directory. Following, this collection is pushed to a List<User> typed collection using AddRange with the department set off the User.Department property. If no department is returned, it is set to a global string literal that represents a broken (no return) query, used later in conditional statements. Probably should use a String.Compare instead of the equality operator but you get the idea.

[csharp]
private static string GetCurrentUserDepartment()
{
string department = string.Empty;
SPSecurity.RunWithElevatedPrivileges(delegate
{
var usersDs = new DirectorySource(_entry,SearchScope.Subtree);
var cleanUserName = Regex.Replace(SPContext.Current.Web.CurrentUser.LoginName,”.*\\\\(.*)”, “$1”, RegexOptions.None);
IQueryable users = usersDs.Where(usr => usr.AccountName == cleanUserName);
// put in to fix LINQ to AD Bug
var userList = new List();
userList.AddRange(users);
if (userList.Count > 0)
{
User curUser = userList.FirstOrDefault();
department = curUser.Department;
}
else
{
department = _breakStatement;
}
});
return department;
}

[/csharp]

Next Section >> Part II – Building The Remainder Of The WebPart Class

Share

SimpleChart (Org Chart WebPart) For SharePoint V2 – Now With Active Directory

I had some time over my lunch break today to put in a lot of fixes, code improvements, and new features. In no particular order of importance, there were three big things done:

Code Improvements I added improved exception handling, principally for resolving particular Types of exceptions, avoiding the use of a catch all Exception type. Beyond that, bloat reduction and reuse in terms of static method extraction, general refactoring, etc.

sharepoint_org_chart_improvements

Exceedingly generic methods are used for everything non-specific to the gathering of the org chart data, such as major rendering methods since values in a DataTable (according to Google Visualization API specifications) are really the only thing changing.

General Fixes With The Last Version People were getting an ArgumentException bubble up in the last version that occurred on particular SharePoint patch levels. This has been mended in this version. It was my fault; I only had one platform to test with at the time because I wrote the first version over a lunch break at a client. But hey, it was labeled in the versioning output as a checked/debug build. :)

Active Directory Integration Yea! This version offers a toggling in the WebPart properties allowing a user to specify which mode they want to run SimpleChart in, either SharePointListMode or ActiveDirectoryMode. Each of these requires the hydration of their relevant properties, i.e. SharePointListMode requires the appropriate SharePoint List and Column Values, and ActiveDirectoryMode requires knowing which domain to query against. I added the Group Restriction option as a property because just laying out a domain in an org chart is frankly ludicrous. For any reasonably sized organization that would hardly be useful. However, it is prevalent within orthodox organizations to have Active Directory groups that are representative of divisions or other logical groupings within a company so this seemed like the most practical approach.

So, for those that like exhaustive illustrations before they download something, here is the WebPart in action.

Firstly, let’s start using SharePoint lists as a datasource follows the same pattern that is detailed in the first version of this WebPart here. That post has the solution install instructions so I am not going to rehash that.

When you first add the WebPart, the default mode it will function in is SharePointListMode. You will be informed that the required properties for the WebPart to function have not been adequately provided.

sharepoint_org_chart_missing_properties

So you have to go fill these in! For this exercise I am using the SharePoint List Template (.stp) as detailed in the last version post as a datasource since it already has the relevant values for testing. When you open the properties, make sure you got the right version deployed by examining the versioning editor part located at the top of pane. Your version should be *2.0.0.1*.

org_chart_sharepoint_new_version

Going off that same template, im going to make a new list off it:

org_chart_datasource

_sharepoint_org_chart_list

Then fill in all the relevant properties for normal functioning based on the list data source. Same as last version, nothing fancy here. Remember to put the list name in too even though the below is simply showing the column hydration for brevity reasons.

org_chart_properties_filled_in

When completed, you will see the org chart rendered! You just made art. Kinda.

list_org_chart

Now it’s time to demonstrate the Active Directory integration. Firstly, you have to examine the new properties that allow the org chart to render out correctly.

  1. Specifies the rendering mode, whether you want the org chart to target either a SharePoint list or an Active Directory data source.
  2. This is still the list to use when in SharePointListMode.
  3. The domain to query into when using Active Directory mode.
  4. The name of the group to restrict the org chart to. This can be a division, grouping, anything really. This allows several org charts to be tailored to specific divisional taxonomy’s rather than making a master domain org chart that provides literally zero value. Unless you have like 5 people in your company.

new_sharepoint_org_chart_properties

Firstly, change the rendering mode to use ActiveDirectoryMode.

ad_org_chart_mode

Now fill in the remainder of the properties based on your environment.

org_chart_ad_filled_in

The group you choose is pretty flexible. Since I am using the Microsoft provided VM I am actually going to change “Guests” t0  “Litware Contractors”. That only has two people so should cultivate a nominal display.

contractors_group

Now, mapping the name in the WebPart properties:

grou_restricted_org_chart

 And now displaying the sexiness that is esoteric business material, the resulting org chart:

AD_org_chart

And that’s it! The WebPart is free, but any backlinks or examples of your use, even in the comments, is always neat because it equates to my satisfaction as a developer.

DOWNLOAD THE SIMPLECHART V2 INSTALLATION PACKAGE

Let me know if you have problems in the comments :)

Share