Friday
May252012

SharePoint in a single page, now with List Item edit

 

Here's the latest screenshot from this project on http://spaspa.codeplex.com

 

 

I've put in buttons that allows you to edit a list item, and that drops the item into edit mode.

Here I'm modifying the Title field.  When you hit Save, it will commit the changes back to SharePoint.

Twitter Bootstrap UI turns out to be really good at guessing screen sizes and adapt.  Here's the page in landscape mode.

image

Thursday
May102012

VS.NET 2008, VSeWSS 1.3 and SafeControl

Oh VSeWSS how I've NOT missed you. 

I was compiling an old solution package for SharePoint 2007, this solution has 6 web parts but the WSP package's manifest only has SafeControl lines for 4 of them.  Somehow, it is discriminating against my newest 2 additions to the family.

In VS.NET 2010, the settings for whether a web part feature should be included in SafeControl is off the WSP Package view.  It automatically adds the current DLL, as well as giving us an option to add more additional assemblies (via the special package solution designer).

image

 

In VS.NET 2008 and SharePoint 2007, the way it determines whether or not to include a Web Part when it generates the manifest file seems...  almost, bizarre and magical.

 

This is how it works

  1. During packaging, VSeWSS reads the DLL to determine if there are web parts within it that needs a SafeControl line.
  2. When it finds them, it modifies a temporary file that it uses during packaging, under project\bin\Debug\solution\manifest.xml
  3. The packaging process than packs up everything under \solution\ into a WSP file, and the manifest file is stored within the WSP file.
  4. What's confusing, is that VSeWSS is not reading the right DLL that's compiled from this project, instead, if you have an older copy of the DLL in the GAC, it will actually read the GAC version over your newly built DLL.

    The end result, is that you think you are crazy and you don't understand why it's arbitrarily deciding to add 4 SafeControl lines, but not all 6.

 

Fix

The fix is pretty simple, if you manually deploy the DLL to the GAC first, then repackage again, it'll pick up the additional web parts within the DLL and add them to your SafeControl list.

 

Why have I never seen this before

If you are deploying to the local machine for testing, you actually may not even see this error - since when you deploy the currently built solution to the local machine it also puts the new DLL into the GAC. 

Thursday
May102012

SharePoint Single Page Application day 4

Did a bit of clean up and finally I've got what I can call the first awesome screen shot.

This is a Single Page Application deployed to SharePoint, and talks to SharePoint via the Client Object Model as well as the REST interface.

The page does not Postback.  It always talk to SharePoint via AJAX.

 

So far:

  • Package 1 ASPX page in a solution package
  • Reference jQuery, Knockout
  • Layout using Twitter's Bootstrap
  • Grabs lists via Client Object Model
  • Grabs list items via REST interface
  • The darn thing is beautiful

 

To come next:

  • CRUD for list items
  • Document download
    • Document upload?
  • List paging
  • Use different icons for lists and document libraries on left navigation
  • A favicon
  • An actual Title for the page
  • Site navigation (this will do post back - since we'll be navigating between say http://server/ to http://server/publishing/ ).
  • Quick search / filter
  • An interesting picture thumb-nail viewer

 

Check it out on http://spaspa.codeplex.com/ and let me know what you think!

Friday
May042012

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!

Friday
Apr272012

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
Friday
Apr272012

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

http://johnliu.net/storage/RestPostcodesForm.xsn

Thursday
Apr262012

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
Saturday
Apr212012

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

http://johnliu.net/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!

Wednesday
Apr112012

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

Wednesday
Apr042012

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 (?)