SharePoint - stripping HTML tags in XSL

Sometimes, when working with XSL (for example, in a content query webpart), you would like to limit the number of characters returned in a field in the template. 

Trimming field length in XSL

The itemstyle template may look something like this:

    <xsl:template name="FAQ" match="Row[@Style='FAQ']" mode="itemstyle">       
        <xsl:variable name="SafeLinkUrl">
            <xsl:call-template name="OuterTemplate.GetSafeLink">
                <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
            </xsl:call-template>
        </xsl:variable>

        <dt>
            <a href="{$SafeLinkUrl}" class="title" onclick="GoToLink(this);return false;" target="_self">
                <xsl:value-of select="@Title" />
            </a>
        </dt>
        <dd>
            <xsl:choose>
                <xsl:when test="string-length(@Answer) &gt; 150">
                    <xsl:value-of select="concat(substring(@Answer,0,150),'...')" disable-output-escaping="yes"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="@Answer" disable-output-escaping="yes"/>
                </xsl:otherwise>
        </xsl:choose>
       </dd>
    </xsl:template>

The interesting part is where the @Answer field is rendered - if the length of this field is beyond 150 characters, it will return the first 150 characters and append an ellipsis (...)

Problem when we have a HTML field

The technique is pretty simple, but it is not fool proof - when the field you are trying to trim is an HTML / Rich field, you have a big problem - the trimming may suddenly cut off valid HTML, to return an invalid HTML.

Imagine, if the @Answer field was:

<div>
     <span>a great answer</span>
</div>

And we perform:

substring(@Answer, 25)

Then we'll get:

<div>
     <span>a great answe
r</span>
</div>

 

You are now returning really bad HTML to the browser:

<dd>
    <div>
        <span>a great answe
...
</dd>

See how the HTML isn't terminated correctly with the proper end-tags.  If you are lucky, the browser guesses correctly and doesn't break your page.  Many times though, it's all down hill from here.

 

Using XSL to strip HTML tags from your field.

A very simple solution is to add an additional XSL template to strip out the HTML tags from your field before rendering it.  A simple version can be found on this article.

 

1.  Add this removeHtmlTags template in your XSL

<xsl:template name="removeHtmlTags">
    <xsl:param name="html"/>
    <xsl:choose>
      <xsl:when test="contains($html, '&lt;')">
        <xsl:value-of select="substring-before($html, '&lt;')"/>
        <!-- Recurse through HTML -->
        <xsl:call-template name="removeHtmlTags">
          <xsl:with-param name="html" select="substring-after($html, '&gt;')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$html"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

 

2. Modify your ItemStyle to use the removeHtmlTags template in an XSL variable

    <xsl:template name="FAQ" match="Row[@Style='FAQ']" mode="itemstyle">       
        <xsl:variable name="SafeLinkUrl">
            <xsl:call-template name="OuterTemplate.GetSafeLink">
                <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="textAnswer">
            <xsl:call-template name="removeHtmlTags">
                <xsl:with-param name="html" select="@Answer" />
            </xsl:call-template>
        </xsl:variable>

        <dt>
            <a href="{$SafeLinkUrl}" class="title" onclick="GoToLink(this);return false;" target="_self">
                <xsl:value-of select="@Title" />
            </a>
        </dt>

        <dd>
            <xsl:choose>
                <xsl:when test="string-length($textAnswer) &gt; 150">
                    <xsl:value-of select="concat(substring($textAnswer,0,150),'...')" disable-output-escaping="yes"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$textAnswer" disable-output-escaping="yes"/>
                </xsl:otherwise>
            </xsl:choose>
       </dd>
    </xsl:template>

Summary

  1. Use substring to strip fields to a certain number of characters - so you don't return everything
  2. For HTML fields, add removeHtmlTags template and use call-template to get the result into an XSL variable first

SPSPerth 2012 Update

This is my first time in Perth!  Hello Perthites!

I even took a train to Cottesloe beach - a very nice beach, I watched the sunset and did what any geek would do, I checked in the Indian Ocean.

I will update this post with links after the sessions. In the mean time, you can reference my current collection of articles on these topics regarding REST, custom service, and Knockout

/rest/

Downloads

Reference

Make sure you follow Matt Menezes
http://spmatt.wordpress.com/

He's a regular at Perth user group circles and knows a lot about Knockout.  I think he is slotted in for a future session in the upcoming months - don't miss it!

SharePoint - Recursive delete SPWeb PowerShell script

Or, what happens when you stuffed up and need to clean up a lot of sites created by accident.

 

Story

  • I have a feature receiver stapled to my site template, it checks a few conditions, does a few things, then creates 1 subsite and stops
  • The new subsite is created, and runs the same receiver, it checks a few more conditions, does a few things.  It shouldn't create any subsites.
  • This afternoon, it did.  The process did not stop.
  • Ouch

 

Problem

  • You can't delete the higher level web object when it has subsites.  You can't follow the subsites because the path has gone way deep.
  • Sounds like time for a good PowerShell script

 

Script

$web = get-spweb http://dev/ourprojects/nsw/1000

function CleanSite( $w )
{
    $ws = $w.Webs;
    foreach( $w1 in $ws)
    {
        CleanSite($w1)
    }
    echo $w.Title
    $w.Delete()

}

CleanSite $web

 

Closing thoughts

My initial fears were that the feature receivers has gone berserk and I'd have hundreds of subsites to clean up.  This turned out to be not the case - I only had about 12 subsites.  I'm wondering if there was a SharePoint error that prevented deeper subsites from being created.  So the disaster really wasn't that bad.  12 subsites I can manage. 

image

SharePoint Saturday 2012 public announcements

 

A new year means more SharePoint Saturdays!

In Australia, first up, is SPS Perth.  This will be held on April 21st.  In just 3 weeks' time. 

This will also be the first time I head over to Western Australia to mingle with the locals.  Hope to do a bit of exploring, and make lots of new friends and followers.

Perth 21/04
Brisbane 02/06 http://spsbne12-eorg.eventbrite.com/
Adelaide 30/06 (?)
Melbourne 04/08 (?)
Canberra 22/09 (?)
Sydney 27/10 (?)

InfoPath, custom WCF Service, Word and Open XML SDK (outline)

This upcoming series of articles promise to be far more exciting than the title.

Scenario

How do you use an InfoPath form document to populate a word document with content controls. 

 

Steps:

  1. Building a Word document template with Content Control place holders
  2. Use Content Control toolkit to bind values to place holders in the template to an embedded custom XML (hey, isn't an InfoPath document an XML file?)
  3. Creating a WCF service to take a Word template document, and replace the custom XML, by the power of Open XML SDK (2.0, of course).
  4. Invoking the WCF service from within an InfoPath document to generate a word template version of itself!
  5. Finally, a bit of discussion on where Word Automation services fits in the bigger picture, as well as thoughts on best practices.


Drop a comment below if you have any specific questions relating to these steps.  Specifically, if you can think of a far better title for this series.

Otherwise, stay tuned!  :-)