General Ramblings comments edit

I went to the post office yesterday to mail off my first eBay sale and was dismayed at the whole experience.

Let me step back a bit, though, and explain some of the lead-up.

I sold one of my shirts on eBay and was trying to figure out the best way to get the item in the mail on time. I got payment through PayPal (which, by the way, turns out to be a total scam if you want to accept credit card payments, but that’s another story), and I told the buyer yesterday morning that I’d get the item in the mail that day.

Facilities at work has a mail scale and postage machine and is nice enough to sometimes let us buy postage from them directly rather than going to the post office. I sent an email to the facilities manager to find out if we could send priority mail with delivery confirmation and insurance from here. He said they could. So I went down to the mail room…

… and no one was there. BAH. The facilities manager has to be there with the key to the postage machine, so no go. I went back to my desk.

I tried about once an hour for a few more hours, but at 2:30p or so, after not finding him there, I sent another email - “When’s the best time to come down and mail this?” The response: “In the morning.” No no no no no! It needs to go out today!

Time to go to the post office.

There are two in the area. One’s a half mile away, one’s five miles away. I went to the one a half mile away…

… to find out that it’s a “detached carrier unit,” which means you can drop your stuff off, but they don’t sell postage. Shit. So I went to the other post office.

Now, you gotta figure - it’s like 3:00p, the lunch rush should be over and I should be able to get through the PO with minimal effort, right? Dude, the line was out the door when I got there. They had three people (out of four possible) working the counters, and it was utter gridlock. It took me, seriously, nearly 45 minutes to get my package mailed. Ridiculous. Why does it take so long?

It occurred to me that going to the Post Office is like going to the DMV. They’re all government workers, which implies they may or may not have incentive to work any faster; there’s no sense of urgency; there’s an underlying level of incompetence that you can’t quite put your finger on; and you don’t have a choice in the matter.

I sat and watched as one of the people working the counter took a package from a person and walked it around for, seriously, like five full minutes trying to figure out which bin it went in. There were only four bins! Pick one!

One customer asked for a particular type of $0.10 stamp. Maybe he’s a collector or something. The clerk didn’t have it. The clerk asked the other clerks; they also didn’t have it. The clerk then went on a ten minute hunt for the stamps, just to find out that they didn’t have any. Come on, people. If you don’t have the stamp, you don’t have stamp. Move on!

And you know what? The whole reason I went there was because the customer I sent the shirt to wanted insurance on a $12 package. Insurance for $12 costs $1.30. Why?! Bah. If she hadn’t wanted insurance, I could have printed my postage off my computer - priority mail with delivery confirmation - and never had to deal with it. Irritating.

General Ramblings comments edit

Aw, crap. I was going to update my “About” section with my new logo but I forgot I haven’t moved it over to PHP so it’s all erring out. How come you people didn’t tell me about this?! I’ll have to fix that.

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).