SharePoint Designer 2013’s Missing Design View – Still

Here is a question that came in from my blog today.

My company is starting to plan to migrate to SP 2013 and I just found out that the design and split view is not included in SP Designer. This is going to be absolutely devasting for us as this is really the only way we do development. We have so many items created with XSL that I can’t imagine what we are going to do when we migrate. Can you update me on if MS has reconsidered adding that back and if not, where do I go to post my comments and to add my voice to those that are not happy?

Unfortunately, questions about the missing Design View in SharePoint Designer 2013 are fairly commonplace and sadly I have no good news. Those views are gone and as far as I know are not coming back. Any uproar that occurred due to my earlier posts like SharePoint Designer 2013′s Missing Design View – It’s Official fell on deaf ears.

I tend to write my own XSL, and one can certainly continue to do so using SharePoint Designer 2013. The Design View was always inexact, but since it was where we saw any error messages, I find that is why I miss it the most.

Display Templates – which are built of markup and script – are worth looking at, as are frameworks like Knockout or AngularJS. Yes, new things to learn, but more the way Web development is headed.

I also don’t know of anywhere to go to register complaints about this. I find that the MSDN Forums may make you feel better, but are rarely monitored by Microsoft people who can push for changes. Microsoft Connect is supposed to be the place to report bugs and give feedback, but things seem to languish there as well.

None of this is much solace for people who need to maintain existing XSL-based solutions. Bottom line: we all need to learn new tricks and/or rewrite our older XSL-based solutions.

Displaying the Most Recent Documents from All Document Libraries in a SharePoint Site

Data View Web Parts (DVWPs) are still my favorite tool in my SharePoint toolbox. Whenever I can use them to get the server to do the heavy lifting before the page is rendered, I do. jQuery and all of the fancy template libraries out there are grand, but in most cases it introduces a lag for the user between the time the page shows up in the browser and the time they can see all of the content.

In this case, I wanted to show the most recently modified documents in each of the Document Libraries in one site on that site’s home page. A DVWP using DataSourceMode=”CrossList” is a great way to do this. By using this method, any new Document Libraries will simply show up in the DVWP as soon as they are added. There’s no need to change any code at all.

I don’t usually like to just give the full code to something because I think it’s more beneficial to show the pieces so that you, gentle reader, must adapt things to your own environment and particular business needs. Given the neutered state of SharePoint Designer 2013, though, I think I’ll probably post full DVWPs where I can going forward. It’s simply too darn hard to edit DVWPs in SPD2013. Here I was working in SharePoint 2010, but the DVWP will work the same, whether you are in SharePoint 2007, 2010, or 2013.

This DVWP will display the Document Library name in a header row and then the three most recently modified documents, whether or not they are in folders. I’m display the name of the document as a link, the Modified By user as a link to their info or User Profile (depending on license version), and the Modified date/time columns. If you’d like to show other columns you can add them into the ViewFields in the DataSource section and then into the XSL. (In a CrossList DVWP, you must explicitly request each column you’d like to use.) To adjust the number of documents displayed per Document Library, just change the value of the TopN param near the top of the XSL section.

<WebPartPages:DataFormWebPart runat="server" Description="" ListDisplayName="" PartOrder="1" HelpLink="" AllowRemove="True" IsVisible="True" AllowHide="True" UseSQLDataSourcePaging="True" ExportControlledProperties="True" IsIncludedFilter="" DataSourceID="" Title="Most Recent Documents" ViewFlag="8" NoDefaultStyle="TRUE" AllowConnect="True" FrameState="Normal" PageSize="-1" PartImageLarge="" AsyncRefresh="False" ExportMode="All" Dir="Default" DetailLink="" ShowWithSampleData="False" FrameType="Default" PartImageSmall="" IsIncluded="True" SuppressWebPartChrome="False" AllowEdit="True" EnableOriginalValue="False" AutoRefresh="False" AutoRefreshInterval="60" AllowMinimize="True" ViewContentTypeId="" InitialAsyncDataFetch="False" MissingAssembly="Cannot import this Web Part." HelpMode="Modeless" ListUrl="" ID="g_2b53a84e_bf26_4583_af64_ac20e428b458" ConnectionID="00000000-0000-0000-0000-000000000000" AllowZoneChange="True" ManualRefresh="False" __MarkupType="vsattributemarkup" __WebPartId="{D2B80E07-81CE-48C5-83A1-D124096DC364}" __AllowXSLTEditing="true" WebPart="true" Height="" Width="">
<ParameterBindings>
  <ParameterBinding Name="URL" Location="ServerVariable(URL)" DefaultValue=""/>
</ParameterBindings>
<Xsl>
<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal">
  <xsl:output method="html" indent="no"/>
  <xsl:param name="URL"/>
  <xsl:param name="TopN">3</xsl:param>

  <xsl:template match="/" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:SharePoint="Microsoft.SharePoint.WebControls">
    <xsl:call-template name="dvt_1"/>
  </xsl:template>

  <xsl:template name="dvt_1">
    <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
    <table border="0" width="100%" cellpadding="2" cellspacing="0">
      <tr valign="top">
        <th class="ms-vh" nowrap="nowrap">Name</th>
        <th class="ms-vh" nowrap="nowrap">Modified By</th>
        <th class="ms-vh" nowrap="nowrap">Modified</th>
      </tr>
      <xsl:call-template name="dvt_1.body">
        <xsl:with-param name="Rows" select="$Rows"/>
      </xsl:call-template>
    </table>
  </xsl:template>

  <xsl:template name="dvt_1.body">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
      <xsl:variable name="NewList" select="ddwrt:NameChanged(string(@ListProperty.Title), 0)"/>
      <xsl:if test="string-length($NewList) &gt; 0">
        <tr>
          <td class="ms-vb" style="background-color:#cfcfcf;" colspan="99">
            <a style="color:#990202;" href="/{substring-after(@FileDirRef, ';#')}"><xsl:value-of select="@ListProperty.Title" /></a>
          </td>
        </tr>
        <xsl:call-template name="dvt_1.body2">
          <xsl:with-param name="Rows" select="$Rows[@ListProperty.Title = current()/@ListProperty.Title]"/>
        </xsl:call-template>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <xsl:template name="dvt_1.body2">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
      <xsl:sort select="ddwrt:FormatDateTime(string(@Modified), 1033, 'yyyyMMdd HHmmss')" order="descending"/>
      <xsl:if test="position() &lt;= $TopN">
        <xsl:call-template name="dvt_1.rowview" />
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <xsl:template name="dvt_1.rowview">
    <tr>
      <xsl:if test="position() mod 2 = 1">
        <xsl:attribute name="class">ms-alternating</xsl:attribute>
      </xsl:if>
      <td class="ms-vb">
        <a href="/{substring-after(@FileDirRef, ';#')}/Forms/DispForm.aspx?ID={@ID}&amp;Source={$URL}"><xsl:value-of select="substring-after(@FileLeafRef, ';#')" /></a>
      </td>
      <td class="ms-vb">
        <a href="/_layouts/userdisp.aspx?ID={substring-before(@Editor, ';#')}"><xsl:value-of select="substring-after(@Editor, ';#')"/></a>
      </td>
      <td class="ms-vb">
        <xsl:value-of select="ddwrt:FormatDate(string(@Modified), 1033, 5)"/>
      </td>
    </tr>
  </xsl:template>

</xsl:stylesheet></Xsl>
<DataSources>
  <SharePoint:SPDataSource runat="server" DataSourceMode="CrossList" SelectCommand="&lt;View&gt;&lt;Lists ServerTemplate='101'&gt;&lt;/Lists&gt;<Query><Where><Eq><FieldRef Name='FSObjType' /><Value Type='int'>0</Value></Eq></Where><OrderBy><ListProperty Name='Title'/></OrderBy></Query>&lt;ViewFields&gt;&lt;ListProperty Name=&quot;Title&quot; /&gt;<FieldRef Name='FSObjType' />&lt;FieldRef Name=&quot;ID&quot;/&gt;&lt;FieldRef Name=&quot;Title&quot;/&gt;&lt;FieldRef Name=&quot;FileRef&quot;/&gt;&lt;FieldRef Name=&quot;FileDirRef&quot;/&gt;&lt;FieldRef Name=&quot;FileLeafRef&quot;/&gt;&lt;FieldRef Name=&quot;Editor&quot;/&gt;&lt;FieldRef Name=&quot;Modified&quot;/&gt;&lt;/ViewFields&gt;&lt;/ViewFields&gt;&lt;/View&gt;" UpdateCommand="" InsertCommand="" DeleteCommand="" UseInternalName="True" UseServerDataFormat="True" ID="dataformwebpart1">
  </SharePoint:SPDataSource>
</DataSources>
</WebPartPages:DataFormWebPart>

Calculate Days between Two SharePoint List Dates in XSL Using ddwrt:DateTimeTick

There was an interesting question over in the MSDN Forums the other day that I struggled to answer. It was a headscratcher, so I had to figure it out. In the thread, emfuentes27 wanted to know why s/he saw different results in SharePoint Designer than in the browser when using this formula:

<xsl:value-of select="number(ddwrt:DateTimeTick(ddwrt:GenDisplayName(string(@Due_Date))))- number(ddwrt:DateTimeTick(ddwrt:GenDisplayName(string($Today))))" />

Not only had I never seen the ddwrt:DateTimeTick function (no documentation on that is available anywhere at all that I can find), but the numbers just didn’t make sense.

[important]The ddwrt namespace functions are incredibly valuable, but they are simply not documented by Microsoft anywhere. There is a single article by Serge van den Oever (@svdoever) from the SharePoint 2003 days which explains it (very well), but that’s really it.[/important]

The ddwrt:DateTimeTick isn’t documented there and I’d never seen it in the wild. At least now I know about it.

To determine the days between two dates in the past, I’ve always used date arithmetic XSL templates, as I explain in my post Date Arithmetic in SharePoint DVWPs. The ddwrt:DateTimeTick function turns out to be a lot easier to use, but as I said, the numbers just didn’t make sense. They didn’t make sense, that is, until I went back to a little arithmetic.

It seems that the values in the browser were off from the ones in SPD (I tested this in WSS 3.0 because I had that VM open) by a factor of 864000000000. Trying to figure out the significance of that, I realized that it’s the number of seconds in a day times 1 million:

864000000000 = 60 * 60 * 24 * 1000000

60 * 60 * 24 = the number of seconds in a day.

Wolfram Alpha helps with this. Try going there and typing in 86400 seconds.

Who knows why this is the case, but this equation will give you the right answer in a browser (it will be wrong in SPD), which is what you are really after in the first place:

<xsl:value-of select="(number(ddwrt:DateTimeTick(ddwrt:GenDisplayName(string(@Lead_x0020_Date)))) - number(ddwrt:DateTimeTick(ddwrt:GenDisplayName(string(@Created))))) div 864000000000" />

The ddwrt:DateTimeTick function is a great tool for your XSL work in SharePoint. It returns the number of “ticks” since Dec 30, 1899 in SharePoint Designer. That’s an odd date to use as the base, but you can test it by adding this to your XSL:

1899-12-30::<xsl:value-of select="ddwrt:DateTimeTick(ddwrt:GenDisplayName(string('0001-01-01')))" /><br/>
1899-12-31::<xsl:value-of select="ddwrt:DateTimeTick(ddwrt:GenDisplayName(string('0001-01-02')))" /><br/>
1900-01-01::<xsl:value-of select="ddwrt:DateTimeTick(ddwrt:GenDisplayName(string('0001-01-03')))" /><br/>

Even odder, the base date in the browser seems to be Jan 1, 0001. You don’t see that date bandied about too often. Go ahead; give it a go:

0001-01-01::<xsl:value-of select="ddwrt:DateTimeTick(ddwrt:GenDisplayName(string('0001-01-01'))) div 864000000000" /><br/>
0001-01-02::<xsl:value-of select="ddwrt:DateTimeTick(ddwrt:GenDisplayName(string('0001-01-02'))) div 864000000000" /><br/>
0001-01-03::<xsl:value-of select="ddwrt:DateTimeTick(ddwrt:GenDisplayName(string('0001-01-03'))) div 864000000000" /><br/>

This is yet another example where the rendering engine in SharePoint Designer doesn’t yield the same results as what the SharePoint server provides. Don’t expect something like this to be fixed any time soon, though, given Microsoft’s abandonment of the Design and Split View in SharePoint Designer 2013.

We don’t really care what the base date is for zero ticks, of course; we just want to be able to use the values to determine the difference in days between two “ticks”. As long as you use the million-day-seconds trick, all is well.

This post also appeared at NothingButSharePoint.com on 2013-01-09. Visit the post there to read additional comments.

SPXSLT (SharePoint XSL Templates) Release 0.0.9

SPXSLTI’ve just released a new version of the SPXSLT Codeplex Project, version 0.0.9.

In this release, I added one new template called FixAmpersands. FixAmpersands is useful when you want to “join’ two lists in an AggregateDataSource in a Data View Web Part (DVWP) and the column you are uisng as the key may contain ampersands. This isn’t all that unusual if the keys are text values that don’t represent some sort of coding scheme, like a Product Code. Sometimes you may need to “join” two column values that contain ampersands, like “Beef & Kidney Pie” or “Tool & Die”. Because of the way that ampersands must be encoded on the Web, the primary value will contain the ampersand & but the secondary value will contain an encoded ampersand &amp;. By calling FixAmpersands on the primary value, the “join” will work because the & will be replaced with &amp;. (In fact, I had to monkey with the representations of the ampersands in that last sentence to get them to display correctly.)

The template is recursive, so it will replace *all* occurrences of & with &amp;.

FixAmpersands looks like this:

<xsl:template name="FixAmpersands">
  <xsl:param name="StringValue"/>
  <xsl:variable name="Ampersand" select="'&amp;'" />
  <xsl:choose>
    <xsl:when test="contains($StringValue, $Ampersand)">
      <xsl:value-of select="concat(substring-before($StringValue, $Ampersand), '&amp;amp;', substring-after($StringValue, $Ampersand))"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$StringValue"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

And you call it like this:

<xsl:varaible name="FixedTitle">
  <xsl:call-template name="FixAmpersands">
    <xsl:with-param name="StringValue" select="@Title"/>
  </xsl:call-template>
</xsl:variable>

In the examples above, here’s what would happen:

Title FixedTitle
Beef & Kidney Pie Beef &amp; Kidney Pie
Tool & Die Tool &amp; Die
Sugar & Spice & Everything Nice Sugar &amp; Spice &amp; Everything Nice

I also fixed a small issue I noticed with the MultiSelectValueCheck.xsl file, which was missing the stylesheet wrapper.

The Content Query Web Part (CQWP) and the ddwrt Namespace

I’m positive I’ve run into this little issue before, but since I don’t seem to have done a post on it, I stymied myself again.

A client of mine spotted a nice post from Ben Tedder (@bentedder) showing how to Create a Classifieds Listing using SharePoint 2010. It uses a bunch of Content Query Web Part (CQWPs) to build up a sort of virtual bulletin board where people can post things they have for sale, help they need, upcoming community events, etc. Ben has been doing a lot of really cool stuff with SPServices, so I’m familiar with his stuff and know that the quality level is high.

Ben’s post gives a nice outline of how to create this type of solution, and we decided I’d implement it. Normally I’d jump straight to the Data View Web Part (DVWP) for something like this, but there’s some appeal to the CQWP in this case. It’s not unlikely that they will want to change both how the categories are laid out on the page and what categories are available, and there’s no reason to involve a developer at that point; the CQWP is pretty easily adjusted by a reasonably sophisticated user.

When I first dropped Ben’s template into the ItemStyles.xsl file, saved it, and refreshed my page, I got an error. No worries, I figured, I must have missed a closing tag or comma or something. About an hour and a half later, I’d tried every version of paring the XSL down that I could think of and had pretty much isolated the issue to where I was displaying the Created date. But I couldn’t for the life of me figure out why it wouldn’t work.

Time for a drive to get some lunch, and within five minutes the answer had occurred to me. (I’m a big proponent of separating myself from the physical location where something has me stumped – I find it lets my mind wander into different territory.)

The problem was that the ddwrt namespace wasn’t registered in ItemStyles.xsl. What that means is that where I was using the ddwrt:FormatDateTIme function, SharePoint had no idea what I was talking about, and it threw an error. When I displayed the Created date without using the ddwrt function, it worked just fine.

[important]If you’re not familiar with the ddwrt namespace, you should be if you write any XSL at all for SharePoint. The only article I’ve ever found which explains it is one on MSDN from Serge van den Oever
which he wrote way back in 2005 called SharePoint Data View Web Part Extension Functions in the ddwrt Namespace. It’s such an important namespace, it always amazes me that this is the only article which explains it on Microsoft’s site.[/important]

Once back in the saddle, I looked at one of my trusty DVWPs and figured out where the references need to be. Here’s the top of the ItemStyles.xsl file before I edited it:

SNAGHTML1de513ac

and after I added the reference to the ddwrt namespace.

SNAGHTML1de594eb

It’s a simple little change, but absolutely necessary if you want to use the ddwrt namespace functions in your CQWPs.

Now, with this post, I’m happy that I won’t paint myself into this corner again, and hopefully it’ll help a few of you to stay out of that corner as well.