Friday, December 15, 2006

Community Server Paged Permissions Grids

The default implementation of Community Server assumes you will have only a handful of roles. This caused me some problems when I added 700 new roles to configure specific admin/user permissions on 350 forums. All of the permissions grids in the control panel are not paged, and neither IE nor Firefox like loading the massive permissions grids into memory.

Thankfully, the answer is quite simple. In order to add paging to all permissions grids in the control panel, you simply make a couple of minor changes to the BasePermissionsGridControl.cs file.

At the bottom of the buildGrid() method you'll see a comment concerning paging just after the call to ApplyUserSettings(). You can delete each line from that point forward since we now want paging.

Next, you need to add an override of ConfigureGrid and add the following single line to the method:


Finally, set the Grid.PageSize property to whatever size you like in the BasePage_Load method (or leave it alone and let the default take over).

Viola! All permissions grids are now paged in the Control Panel.

Submit this story to DotNetKicks

Tuesday, December 12, 2006

Exchange Distribution Lists and WebDAV

Download Sample Code

I encountered an interesting side project recently where I wanted to retrieve information about distribution lists on a local Exchange Server from a web-based application.

I didn't want to use CDO/MAPI directly for various reasons, so I looked into the WebDAV api available for querying Exchange via web requests. I found some helpful information here, and was quickly able to produce a class to consume distribution list data for use in my app.

The basic query for retrieving all distribution lists from a particular folder (where folder is the RootURI below) is:

<?xml version=\"1.0\"?>

<D:searchrequest xmlns:D = "DAV:" >

<D:sql>SELECT "urn:schemas:contacts:cn\", "urn:schemas:contacts:members\", "DAV:displayname" FROM RootURI WHERE "DAV:ishidden" = false AND "DAV:isfolder" = false AND "DAV:contentclass" = 'urn:content-classes:group'</D:sql>


DAV content-classes are found via mappings from PR_MESSAGE_CLASS and PR_CONTAINER_CLASS properties. The schema references to specific properties in Exchange are derived from the Exchange Server schema. In the above example, we want the property cn from the urn:schemas:contacts namespace. The uri we query should contain contact items whose content-class is of type group.

Now, in order to figure out where all these properties live, and what you can expect can be challenging. You can start with the properties reference by namespace on MSDN. These will tell you the content-class. Or, as I did here, you can use a tool like IndependentDav to get a look at what is returned from the server. I don't think the tool is necessary once you have a handle on where things come from, but it can difficult to track some things down because mappings between Exchange Store schema content-classes and DAV content-classes are not 1:1 and the properties reference gives the content-class of the Exchange Store schema. Finally, you can query the store at a uri without limiting the return values, and look through the query results manually.

Getting on with the distribution lists then, I created a helper class to make the requests and manage the two part process of first constructing a DistributionList class, then filling the members of the list with a seperate query. I pass the streamed response from the query directly to the constructor of the DistributionList class. The code is long and cumbersome, so it's better to just look at it in the sample.

The list members are actually not found via webDAV directly, but a query to the OWA server services as described here. I created a ListMemberList class to hold ListMember classes. The constructor on the list parses each member in the response, and set it as a property on the DistributionList class.  

When its all said and done, I can use the management class to get the DL's from the configured server with very little overhead, and - best of all - no interop.

Submit this story to DotNetKicks