gists, csharp, sharepoint comments edit

Okay, so, like, what happens if you’re writing a web part that you want to work on both Windows SharePoint Services and SharePoint Portal Server? First, you have to determine the web part’s context - are you running in WSS or SPS? Doing that, you can conditionally display things like input fields for audience information, etc. (I dwell on audience stuff because that seems to be the biggest thing that I’d like to use in my web parts yet still want to use the parts on WSS, which doesn’t support audiences.)

The problem is, if you determine that you are running in SharePoint Portal Server and then start calling objects in the Microsoft.SharePoint.Portal namespace, that works great on the SPS box, but gives you compile-time binding errors on WSS. What to do? System.Reflection to the rescue. Late bind to the assemblies and use the System.Reflection namespace to call methods and properties on your late-bound objects.

That sounds like a big pain, and it is. That’s why I’m helping you out by putting up some code here:

/// <summary>
/// Gets the list of audience names and GUIDs for the current site
/// </summary>
/// <returns>
/// A SortedList object where the keys are the audience names (string)
/// and the values are the audience GUIDs (System.Guid)
/// </returns>
public SortedList GetAudienceAndGuidList()
{
    var audList = new SortedList();
    var assemblyInstance = Assembly.LoadWithPartialName("Microsoft.SharePoint.Portal");
    if (assemblyInstance != null)
    {
        // We're working on a SharePoint Portal Server, or a WSS site on a server with SPS
        // Get the current portal context
        var portalContext = assemblyInstance.GetType("Microsoft.SharePoint.Portal.PortalContext");
        var currentContextProperty = portalContext.GetProperty("Current");
        var currentContext = currentContextProperty.GetValue(null, null);
        if (currentContext != null)
        {
            // We have a portal context, so we're on SPS
            // Get the types we'll be working with
            var audMgrType = assemblyInstance.GetType("Microsoft.SharePoint.Portal.Audience.AudienceManager");
            var audCollType = assemblyInstance.GetType("Microsoft.SharePoint.Portal.Audience.AudienceCollection");
            var audType = assemblyInstance.GetType("Microsoft.SharePoint.Portal.Audience.Audience");
            // Create the AudienceManager object
            var audMgr = audMgrType.InvokeMember(null,
                BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { currentContext });
            // Call the Audiences property to get the collection of audiences from the AudienceManager
            var audColl = (IEnumerable)audMgrType.InvokeMember("Audiences", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty, null, audMgr, null);
            // Get the name and GUID for each audience and put them in the collection
            foreach (var aud in audColl)
            {
                try { var audName = (string)audType.InvokeMember("AudienceName", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty, null, aud, null); var audGuid = (Guid)audType.InvokeMember("AudienceID", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty, null, aud, null); audList.Add(audName, audGuid); }
                catch (Exception exc)
                {
                    // If there are any problems, don't add the audience to the list
                    System.Diagnostics.Debug.WriteLine("EXCEPTION getting audience and guid list: " + exc.Message);
                }
            }
        }
    }
    return audList;
}

/// <summary>
/// Uses System.Reflection to late-bind to the Microsoft.SharePoint.Portal assembly and determine if
/// a user is a member of an audience.
/// </summary>
/// <param name="username">The username of the person to look up</param>
/// <param name="audienceID">The audience ID to check membership for</param>
/// <returns>True if the user is a member of the specified audience; false otherwise</returns>
public bool UserIsMemberOfAudience(string username, Guid audienceID)
{
    var assemblyInstance = Assembly.LoadWithPartialName("Microsoft.SharePoint.Portal"); if (assemblyInstance != null)
    {
        // We're working on a SharePoint Portal Server, or a WSS site on a server with SPS
        // Get the current portal context
        var portalContext = assemblyInstance.GetType("Microsoft.SharePoint.Portal.PortalContext"); var currentContextProperty = portalContext.GetProperty("Current"); var currentContext = currentContextProperty.GetValue(null, null); if (currentContext != null)
        {
            // We have a portal context, so we're on SPS
            // Get the types we'll be working with
            var audMgrType = assemblyInstance.GetType("Microsoft.SharePoint.Portal.Audience.AudienceManager");
            // Create the AudienceManager object
            var audMgr = audMgrType.InvokeMember(null, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { currentContext });
            // Call the IsMemberOfAudience method to determine audience membership
            var userIsMember = false; var isMemberOfAudienceArgs = new object[] { username, audienceID }; try { userIsMember = (bool)audMgrType.InvokeMember("IsMemberOfAudience", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, audMgr, isMemberOfAudienceArgs); }
            catch (Exception err)
            {
                // Do nothing
                System.Diagnostics.Debug.WriteLine("EXCEPTION determining audience membership:\n" + err.Message);
            }
            return userIsMember;
        }
        else
        {
            // We're working in WSS, which doesn't have audiences, so we'll assume that everyone
            // is a member of every audience.
            return true;
        }
    }
    else
    {
        // We're working in WSS, which doesn't have audiences, so we'll assume that everyone
        // is a member of every audience.
        return true;
    }
}

General Ramblings comments edit

My first ever sale on eBay happened yesterday around 5:30p, just six hours after I relisted. Apparently it’s true - people don’t want 10 shirts all at once, they want one shirt at a time, and they’ll pay more per shirt to buy individually. Score!

Now I just need to get my ass to the Office Depot to pick up some envelopes to mail these things in. I have a scale at home that’ll weigh up to five pounds (it’s a kitchen scale, but that’s okay) so I’ll be able to calculate and print shipping at home from the USPS site (as long as delivery confirmation is good enough; I have to go to the post office if the person wants insurance).

personal, net comments edit

I just finished my MS 070-300 test, Analyzing Requirements And Defining Microsoft .NET Solution Architectures, and I PASSED. That means I’m now a Microsoft Certified Solutions Developer in .NET. According to MCP Magazine, as of November 6, 2003, there are only 4,711 people in the world with that certification, so it still might hold a little weight.

Regardless, I passed what I figured to be probably the hardest of the tests in the MCSD.NET series. It was case studies with questions over each case study rather than a simple set of questions (which is what all my others were). I don’t like the case studies because they sort of imply certain “right” and “wrong” answers that may or may not be quite what you do in real life. They’re very… subjective. In other tests, it was much more clear which answers were “right” and “wrong.” If the answers were skewed a certain way, it was kind of obvious from the question which way they would want you to skew them. Not so in this latest test, and I think that was the tough part.

I suppose it’s moot now. I PASSED!

General Ramblings comments edit

I’ve been meaning to say something here about this travesty for a while, but I’ve kept forgetting.

There’s this booger on the wall next to the sink in the men’s restroom here at work and it’s been there for probably a couple months now. It was there when I left for training in mid-December, and it’s still there. It’s probably half a centimeter in diameter.

I tried taking a picture of it (I was going to put a ruler next to it so you could see how huge it is) but my camera doesn’t do close-up very well, so it was just a dark blur on the wall.

I have only two questions about this. First, who was so unhygienic as to flick a fucking booger on the wall? Second, how come housekeeping hasn’t dealt with it?!?!

home comments edit

I got my Linksys WRT54G Wireless-G Router hooked up and locked down - no SSID broadcast, MAC address filtering, 128-bit WEP encryption… so wireless networking is in the hizzouse! I’ve tested it out using my laptop from work and it’s awesome.

While I was doing that, I noticed there was a new wireless network called “Ladybug” in my area, broadcasting its SSID. I didn’t try to connect, but I find that interesting since the average age in the apartment complex is like 90. I’m thinking it has to be the guys next door; they’re the only ones I can imagine having such a thing (they’re probably in their mid 30’s).

Anyway, I’m stoked. Wireless networking, router/firewall installed. Set to frickin’ GO.