SharePoint Saturday Boston – March 14, 2009

Another name drop and tip ‘o the hat to Mauro Cardarelli for bringing this one to my attention.  SharePoint Saturday is coming to Boston.  I’m going to see if I can’t get in as a speaker to talk about my SharePoint Designer DVWP stuff.  Working title: Developing with SharePoint Designer: The Middle Tier, Focus on Data View Web Parts (DVWPs).  I know it’s a mouthful, but if all goes according to plan, I’ll be putting together some more Focus on… bits in the future.

What topics would you like to hear me cover?  Of course, if I do speak I’ll make the materials available for those of you out on the ‘Net.

If you’re in the New England area and can afford a Saturday in your schedule, you should check SharePoint Saturday out.  It’ll be held at the Microsoft office in Waltham, MA on March 14, 2009.

SharePoint Saturday is sponsored by the gang over at the International SharePoint Professionals Association.

PerformancePoint Moving into the SharePoint World

My colleague Mauro Cardarelli posted about this the other day:

By now, you have probably heard about Microsoft’s decision on the future of PerformancePoint Server (Microsoft Business Intelligence Announcement Q&A).  Kudos to friend Chris Webb for breaking the news… at least to me!

For PerformancePoint jocks, this is probably bad news, but as a SharePoint junkie, it sounds great.  Having even more Business Intelligence (BI) capabilities available in the SharePoint platform can only be good news.

Mauro’s concerns about his closet space aside, he’s right about the dearth of good talent working with SharePoint today.  But will this create a bigger mess or a bigger set of opportunities?  I think the latter.  This will allow organizations that already are using SharePoint to extend that use even further into the BI space.  Many organizations are already storing some of the data that can feed their BI in SharePoint, so the layering on of more tools simply gives them more options.  Yes, it will make the SharePoint platform bigger and potentially more confusing, and no, there aren’t a lot of folks out there who even understand all of what is included currently (myself included — it’s just too big!), but I expect that we’ll see more specialization inside the SharePoint space to make up for it.

Mauro is certainly right (as usual) that it’s some of the “softer” stuff (strong governance, solid information architecture, tight security models) that make or break SharePoint success.  Those things will become even more important as SharePoint’s capabilities expand.  As I always tell my clients, the best SharePoint developer is one who knows when *not* to code, but to take a step back and talk about the underlying business goals, processes, and incentive changes which are going to be required for the technology implementation even to make sense.

Search Results XSL Question

I had a question from a colleague today about how to do an XSL transformation of some search results.  Here’s the query:

Basically, I want to do the following in XSL:

  1. Parse a managed property, urlEncoded, that has the format of http://[doc library]/[filename] to separate path and filename
  2. Take that prefix and concatenate it with ‘/Forms/DispForm.aspx?ID=’
  3. Take that and concatenate it with another managed property, owsidinteger

All this in one big ugly xsl statement.

This is actually pretty simple, if you know how to do it!  Let’s assume some data for urlEncoded like this:

  • http://servername.com/doclib/doc1.doc
  • http://subdomain.servername.com/doclib/doc1.doc
  • http://subdomain.servername.com/site1/site2/site3/doclib/doc1.doc

Those are the three big levels of complexity.  Visiting the excellent reference MDSN article about the ddwrt namespace by Serge van den Oever, we can see that the UrlDirName function may do the trick:

public string UrlDirName(string szUrl);

Returns the directory name of the file in the given URL szUrl. For example, if szUrl is "/a/b/basename.ext", the value "/a/b/" is returned.

With the data above, UrlDirName will return the following:

  • /doclib
  • /doclib
  • /site1/site2/site3/doclib

So the ‘big ugly XSL’ answer is:

<xsl:value-of select="concat(ddwrt:UrlDirName(string(urlEncoded)), '/Forms/DispForm.aspx?ID=', owsidinteger)"/>

The syntax for the hyperlink is something like:

<a href="{concat(ddwrt:UrlDirName(string(urlEncoded)), '/Forms/DispForm.aspx?ID=', owsidinteger)}">
<xsl:value-of select="@Title"/>
</a>

Note the squiggly brackets.  I think the most common ‘gotcha’ with using the ddwrt functions is forgetting to do the explicit string conversion.

Site Column Name Truncation in Custom Lists vs. Document Libraries

I think I’ve found an interesting (if you think things like this are interesting) difference between Document Libraries and Custom lists when it comes to the use of Site Columns.  I had some filtering code in a DVWP that ought to have worked the same way on both a Custom List and a Document Library, but it turned out that the underlying column names in the Custom List were truncated but they weren’t in the Document Library.

Here’s what I mean.  I added a Site Column called ‘Management and Administration’ to a Custom List and a Document Library.  (I actually added it to a Content Type used by the Document Library and directly to the Custom List, but that turned out not to be the actual distinction.)  The underlying column name in the Document Library ended up as @Management_x0020_and_x0020_Administration and in the Custom List it was @Management_x0020_and_x0020_Admin.  This caused my filtering code not to work for the Custom List.

The simple workaround was to have two separate XSL templates for my filtering which were fundamentally identical, but took into account the slight difference in the column names.  I ended up calling the two templates “ContentFilterDocLibs” and “ContentFilterLists” to make the distinction clear for future developers.

The truncation in Custom Lists always seems to happen at 20 characters, treating the space as a single character (not the 7 characters in the encoded _x0020_ string).  I’m guessing that this is one of those little things that is still around from having had different sets of developers working on WSS vs. MOSS over the earlier versions of SharePoint.

Recursive XSL Templates in DVWPs

I’ve written quite a few recursive XSL templates recently, so I thought I’d do a post explaining what they are and how useful they can be.  On my current project, we’re using many Lookup Columns in Content Types which are based on items in Custom Lists.  These Lookup Columns also are almost all multi-select, meaning that the user can select zero to n of the values to categorize the item they are working with.

One of SharePoint’s quirks is how it then stores the values that the user has selected.  Generally, it looks like this: value1;value2;value3.  No problem, really, as long as you are OK with seeing the semicolon separated values in your displays.  However, if any of the values contains an apostrophe character (‘ or &apos;), it will be stored in the column as &amp;#39;, like so: value1&amp;#39;;value2;value3   (It’s actually that string without the “amp;” part, but I can’t get WordPress to let me show it without it converting to the ‘ character.)  SharePoint natively knows how to handle this, but if you are displaying the column in a DVWP, you may want to do a little work yourself to clean things up, and this is where a recursive XSL template comes in.  (Simply adding the disable-output-escaping=”yes” parameter will make the display look right, but here I want to use the values in the column for some filtering, so the &amp;#39; is a problem.)

A recursive XSL template isn’t really all that different than a recursive function in any other language.  The template can call itself based on some condition multiple times as long as it is capable of “unwinding” itself at the end of the process.  In this example, we want to remove all of the &amp;#39; strings and replace them with the ‘ character.  We don’t know how many of these strings there might be, nor do we want to care about it; we want the template to take care of it for us.

The template below works like this.  We call it, passing in the MultiSelectValue string that may or may not have the &amp;#39; string in it.  The xsl:choose has an xsl:when condition that is true if the &amp;#39; string is present.  If it is, we call the template again with the &amp;#39; string snipped out (substring-before the string plus the apostrophe plus substring-after the string), and so on.  Once there are no more &amp;#39; strings, the template will “unwind”, passing out the final string we want each time.

<xsl:template name="FixEscapedChars">
  <xsl:param name="MultiSelectValue"/>
  <xsl:variable name="Apostrophe">'</xsl:variable>
  <xsl:choose>
    <xsl:when test="contains($MultiSelectValue, '&amp;#39;')">
      <xsl:call-template name="FixEscapedChars">
        <xsl:with-param name="MultiSelectValue" select="concat(substring-before($MultiSelectValue, '&amp;#39;'), $Apostrophe, substring-after($MultiSelectValue, '&amp;#39;')) "/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$MultiSelectValue"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Note that you can handle other encoded characters by simply adding a new xsl:when clause for each to this template.