SharePoint 2010 as a Single Page Application (teaser)

What is a Single Page Application?  Essentially, an application disguised as a single web page.  Like the official "Twitter" web application.  You click, things happen, data is loaded and released asynchronously, layouts are arranged and displayed.  Like magic.

On a crazy Thursday afternoon I had the mad idea of building a Single Page Application on top of SharePoint.

This is a sneak preview of my work so far on this goal:

 

The story so far

  • Everything in a SharePoint farm solution
    • Deploys to /_layouts/JL-SPA/SPA.aspx - one single page!
  • UI layout using Twitter Bootstrap - what you see above is based on the Fluid example template
    • Twitter Bootstrap is based on Less.js
  • UI binding using Knockout.JS
  • Additional glue with jQuery
  • Reading from SharePoint via OOTB REST service API
    • The list of libraries from my current site is returned and bound to the list on the left (see above picture).  I call it a mini success in setting up the scaffolding.  Will continue another day.
  • Exposes _spPageContextInfo object
  • Expose SP.ClientContext objects/scripts

 

This is the entire project so far:

image

 

Still to come

  • UI for lists
    • Sorting for lists.
    • Determine columns for lists.
  • UI for viewing items
  • UI for editing items
  • ?  Add SPServices?  There are some pretty useful workflow use cases in this library.

 

Call for help!

I think I might be out of my mind on this one.  If you want to join in and hack some JavaScript, yell out and help me out! 

Great opportunity to learn some really cool technology and have a lot of fun!

InfoPath - creating a custom FormServer page to embed your own JavaScript goodness

 

From time to time, people ask about how to embed JavaScript within InfoPath - there are all kinds of reasons why you'd want to do this, and in this example, I'll focus on a previous use case (Disabling backspace key in InfoPath browser form) that I've discussed - to disable the backspace key when you don't have a textbox selected - so that the user isn't dumped back to the previous page in Internet Explorer - a terribly woeful user experience.

Steps

  1. Create a SharePoint solution in VS.NET
  2. Add mapping for Layouts
  3. Copy contents from the OOTB FormServer.aspx file to our custom aspx page
  4. Deploy to see how it works
  5. Add our own, additional JavaScript

 

1. Creating a VS.NET SharePoint Project

 

The key point here is that you will need the CKS extensions for VS.NET 2010.  Grab them first from Extension Manager.

image

Because we are going to build an application page that is deployed into SharePoint Root, this project has to be a farm solution.

image

 

2. Map to _layouts in your SharePoint solution

image

image

 

3. Add our custom aspx page

image

You may get an security warning from VS.NET to trust CKS - allow this.

The Basic Site Page is the simplest page that can be added to a SharePoint solution.  It is essentially one ASPX page without any code behind.  In this example this page is sufficient for our needs.

 

At this point, we should go visit our own SharePoint root and see how the default FormServer.aspx page is built.

Navigate to: C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\

And look for FormServer.aspx

You'll see that the page is actually really simple - all the hard work is in the XmlFormView control.  The page passes query parameters to the XmlFormView control.

Copy all the content of this out of the box FormServer.aspx file

Return to our project, and paste all the contents over the sample JLFormServer.aspx file.  Now, your JLFormServer.aspx file should be exactly the same as the out of the box FormServer.aspx file.

image

 

4. Deploy to see how it works

Package and deploy the solution to SharePoint - you'll see this deployed to SharePoint root.

image

 

Take a normal form server url:

http://spg-dev-jl/_layouts/FormServer.aspx?XsnLocation=~site/Forms/Forms/template.xsn&DefaultItemOpen=1

Change it to our custom aspx page:

http://spg-dev-jl/_layouts/JLFormServer/JLFormServer.aspx?XsnLocation=~site/Forms/Forms/template.xsn&DefaultItemOpen=1

image

 

Hey works fine :-)

 

5. Adding our own JavaScript goodness

Go back to our JSFormServer.aspx page, and look for the <head> element.  Let's add some JavaScript.

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js"></script>
<script>
    function document_keydown(e) {

         if (e.keyCode == 8){

            var t = e.target;
            if (t) {
                if (t.tagName == "INPUT") return; // ignore textbox
                if (t.tagName == "TEXTAREA") return; // ignore multi-line textbox
            }
            if ($(t).hasClass("ms-inputuserfield")) {
                return; // ignore people picker, by checking for ms-inputuserfield css class
            }

            // letting us know we've ate a backspace key 
            alert('Ate a backspace key, hew!', false);

            // cancel backspace navigation
            e.preventDefault();
            e.stopImmediatePropagation();
            return false;
        }
    };

    $(document).keydown(document_keydown);
</script>

Package everything and deploy to server.

 

Result: everything in action!

 

Navigate to our custom form server page again.

http://spg-dev-jl/_layouts/JLFormServer/JLFormServer.aspx?XsnLocation=~site/Forms/Forms/template.xsn&DefaultItemOpen=1

Select a textbox and delete a few characters - this should work fine, as the JavaScript function allows this.

Un-focus from a textbox, so that the focus is now on the main page - and press backspace.  Normally, this would trigger Internet Explorer to go back to the previous page in history, but now this event is caught by our JavaScript and cancelled!

image

 

Note

Finally, comment out the alert in your JavaScript - I'm sure you don't want to drive your users crazy.

// letting us know we've ate a backspace key
// alert('Ate a backspace key, hew!');

 

Summary

  • We looked at how to build and deploy a simple site page to SharePoint root
  • What a FormServer.aspx page looks like
  • How we can add additional JavaScript to our Custom FormServer.aspx page

 

Update

  • Updated the javascript function that includes my latest tweaks for multiline textbox and people picker

InfoPath 2010 - query using the REST interface

 

This is an exercise to play with the REST interface, which works extremely well with InfoPath 2010.

Scenario

  1. We have a postcode list.  Filled with post codes and suburbs.
  2. In InfoPath, we want to select a post code and populate the corresponding data from the list.
  3. While this is possible using the old owssvr.dll trick, Let's do this one with the REST interface - which is a much cleaner example.

 

Steps

  1. Here's a very simple custom list, "Postcodes"
    image
  2. The REST interface to select a row, using a filter:
    http://spg-dev-jl/_vti_bin/listdata.svc/Postcodes?$filter=Code eq '2000'
  3. This returns the following XML - you can see Title and Code in the XML
    image
  4. Create an InfoPath 2010 form, create a few fields:  "Postcode" (number) and "Suburb" (text, shown as expression)
    image
    image
  5. Create a secondary data source and connect to the earlier REST service.
    image
  6. Add an action to Postcode - on change, set the REST URL, then execute the query.
    image
    The Change REST URL action appears when you have REST secondary data sources in your 2010 form.

    We want to change the URL based on a formula, so set it to:
    concat("http://spg-dev-jl/_vti_bin/listdata.svc/Postcodes?$filter=Code%20eq%20'", . ,"'")

    image
    image
  7. Finally, copy the new suburb back into our Suburb field from the updated secondary data source.
    image

    image

  8. The completed rule(s)
    image

 

Result

Running in InfoPath rich client

image

Running in Form Server

image

 

Summary

  • The REST data source is actually a FileQueryConnection and expects an XML result.  In InfoPath 2010 though, we have the new action to "change" the path using rules which is really the unsung hero of this story.
  • And thus we got to play with REST and InfoPath 2010

 

Download

https://static1.squarespace.com/static/5527bff2e4b0b430660b0d10/5527c30de4b030eeeef09715/5527c30fe4b030eeeef09eff/1335456086383/RestPostcodesForm.xsn

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!