List View Extension Points (CSR)

Applies To: SharePoint 2013, 2016, Office 365

List View Client Side Rendering Primer: 4 of 5

About This Series

This series provides a brief overview of Client Side Rendering for List Views (often referred to as JSLink). Basic extension points and examples are included. The goal of this series is to get developers unfamiliar with this programming model quickly up to speed.

How to Get the Code

The example created using the steps in this series can be found in full as part of the csrShim repository:
https://github.com/thechriskent/csrShim/tree/master/Examples/CSR%20Primer

The full project can be found on GitHub:
https://github.com/thechriskent/csrShim

You can download or clone the repository directly from that site. You can also view the code directly on the site. csrShim is not used in this example and is not required.

In the following examples, we will be targeting on premise SharePoint 2013 using SharePoint Designer 2013.

List View Extension Points

You can override one or more parts of the rendering of your list view. You do not need to specify all the following, but be aware that specifying a single template will use the default render for the other templates (this is different from the OOTB view).

CTX

The CTX object contains all the information about your list view – including all the returned data. This object is provided to the event callback functions and to any template functions.

Although there are many properties (especially when using standard CSR vs csrShim), here are the key properties to pay attention to:

  • CurrentItem
    • This property is only set when passed to the Item template
    • Provides quick access to all properties for the current item
    • Properties match the internal name of the field
    • Properties can sometimes contain periods and therefore these properties cannot be accessed using the standard dot notation
      //Standard Access
      var created = ctx.CurrentItem.Created;
      
      //Access Properties with Periods
      var createdF = ctx.CurrentItem["Created.FriendlyDisplay"];
      
  • firstRow
    • When true, the item is the first item returned
  • lastRow
    • Only available with csrShim!
    • When true, the item is the last item returned
  • Row
    • The array of returned items with their properties
    • These are equivalent to CurrentItem objects
    • This allows access to ALL rows from any template including the Item template
  • FirstRow
    • Available with standard CSR and csrShim with Lists (not feeds/XML)
    • The item ID of the first item returned
  • LastRow
    • Available with standard CSR and csrShim with Lists (not feeds/XML)
    • The item ID of the last item returned
  • ListSchema
    • In standard CSR, contains several properties about the underlying list structure and the view configuration
  • Field
    • Array of the fields returned with the view
    • In standard CSR, several properties (like DisplayName) can be very helpful for dynamic displays. csrShim only provides the Name (internal name).

Event Callbacks

There are 2 events (OnPreRender and OnPostRender) where you can provide callback functions.

OnPreRender

To have a callback function executed BEFORE your templates are applied to the list data, you can specify an anonymous or named function to the OnPreRender property of your CSR object.

The PreRender event can be helpful to declare any necessary SOD dependencies, verify existence of elements, contact additional web service, etc.

(function(){

    var myCSR = {
        BaseViewID: 1,
        ListTemplateType: 100,
        OnPreRender: myFunction
    };

    function myFunction(ctx){
        //Do Stuff
    }
})();

Example

In our HallOfFame.js example from earlier we will add a console message so we know when our rendering is about to start:

    var HallOfFame = {
        BaseViewID: 1,
        ListTemplateType: 100,
        OnPreRender: hofPreRender
    };

    function hofPreRender(ctx){
        if(window.console && window.console.log){
            console.log('Begin Hall Of Fame!');
        }
    }

Here’s what we just did:

  • Line 5 we added a comma to ensure we could add another property
  • Line 6 we added the OnPreRender property and gave it a reference to our function hofPreRender
  • Line 9 this is our callback function
  • Line 10-12 safely logs a message to the console (the checks prevent IE from throwing errors when the console is not visible)

Refreshing the page with the developer tools visible (F12) should show our message. If you don’t see a console message, verify the JS Link property is pointing to the correct page. Also note that console messages logged before the developer tools were open (in IE) will not show and you may need to refresh.

OnPostRender

To have a callback function executed AFTER your templates are applied to the list data, you can specify an anonymous or named function to the OnPostRender property of your CSR object.

The PostRender event can be helpful to attach events or run any other code dependent on your HTML being in place.

(function(){

    var myCSR = {
        BaseViewID: 1,
        ListTemplateType: 100,
        OnPostRender: myFunction
    };

    function myFunction(ctx){
        //Do Stuff
    }
})();

Example

In our HallOfFame.js example from earlier we will add another console message so we know when our rendering is all done:

var HallOfFame = {
        BaseViewID: 1,
        ListTemplateType: 100,
        OnPreRender: hofPreRender,
        OnPostRender: hofPostRender
    };

    function hofPreRender(ctx){
        if(window.console && window.console.log){
            console.log('Begin Hall Of Fame!');
        }
    }

    function hofPostRender(ctx){
        if(window.console && window.console.log){
            console.log('Finished Hall Of Fame!');
        }
    }

Here’s what we just did:

  • Line 6 we added a comma to ensure we could add another property
  • Line 7 we added the OnPostRender property and gave it a reference to our function hofPostRender
  • Line 16 this is our callback function
  • Line 17-19 safely logs a message to the console (the checks prevent IE from throwing errors when the console is not visible)

Refreshing the page with the developer tools visible (F12) should show both our PreRender and PostRender messages in the proper order.

Templates

There are several templates that can be specified using the Templates property of your CSR object. You can specify simple strings, “magic” strings, and full functions that return a string.

(function(){

    var myCSR = {
        BaseViewID: 1,
        ListTemplateType: 100,
        Templates: {
            Header: "
<div>I'm the Header!</div>
",
            Item:   "
<div>I'm an Item!</div>
",
            Footer: "
<div>I'm the Footer!</div>
"
        }
    };

})();

Available Templates

  • Header
  • Item
  • Fields
  • Footer

Note – The Footer template is often misused to close any open tags from the header or item templates. This will result in invalid HTML. Although most modern browsers will auto close these open items for you, this may cause hard to track down display issues! The Footer template is rendered inside a div element outside the hierarchy of the Header and Item templates and is designed for standalone paging. It can be used for other things, but it is separate from anything generated in the other templates.

Magic Strings

In the most basic forms, templates can be specified as simple strings (as in the example above). Additionally, when specified as strings you can use special notation to have “magic strings” resolved automatically.

(function(){

    var myCSR = {
        BaseViewID: 1,
        ListTemplateType: 100,
        Templates: {
            Header: "
<div>My BaseViewID is <#=ctx.BaseViewID#></div>
"
        }
    };

})();

The format is to include <#=ctx. Before the name of the property and followed by #>

In nearly every case you will be better supplying a function to the template properties (even for simple strings) since this will give you more flexibility, better readability, and easier extensibility.

Functions

You can specify anonymous or named functions (named functions are best practice coding) to each of the template properties. These functions will be provided with the CTX object as the parameter.

(function(){

    var myCSR = {
        BaseViewID: 1,
        ListTemplateType: 100,
        Templates: {
            Header: myFunction
        }
    };

    function myFunction(ctx){
        //Do Stuff
    }
})();

Example Header

Although it would be better to use an external stylesheet, for this simple example we will be declaring an inline style block to set our CSS for the rest of the list view. We will do this in the header. Additionally, we’d like to show a title box.

    var HallOfFame = {
        BaseViewID: 1,
        ListTemplateType: 100,
        OnPreRender: hofPreRender,
        Templates: {
            Header: hofHeader
        },
        OnPostRender: hofPostRender
    };

    function hofHeader(ctx){
        var styles = '
<style type="text/css">' +
                     '.halloffame{' +
                     ' text-align:center;' +
                     ' width:504px;}' +
                     '.halloffame div{'+
                     ' box-sizing:border-box;}' +
                     '.hof-header{' +
                     ' font-size:18px;' +
                     ' font-weight:bold;' +
                     ' border:solid black 4px;' +
                     ' padding:6px;}' +
                     '.hof-items{' +
                     ' border-right:solid black 4px;}' +
                     '.hof-items:after{' +
                     ' clear:both;' +
                     ' content:"";' +
                     ' display:block;}' +
                     '.hof-item{' +
                     ' float:left;' +
                     ' width:125px;' +
                     ' border-left:solid black 4px;' +
                     ' border-bottom:solid black 4px;' +
                     ' height:100px;}' +
                     '.hof-item div{' +
                     ' color:black;' +
                     ' font-weight:bold;' +
                     ' font-size:15px;}' +
                     '.hof-item:hover{' +
                     ' filter:brightness(75%);}' +
                     '.hof-footer{' +
                     ' border:solid black 4px;' +
                     ' border-top:none;' +
                     ' padding:3px;' +
                     ' font-size:10px;}' +
                     '</style>

';
        var header = '
<div class="halloffame">
<div class="hof-header">- Hall of Fame -</div>
<div class="hof-items">';
        return styles + header;
    }

Here’s what we just did:

  • Line 7 we added a Templates property
  • Line 8 we added a Header property to our Templates object and gave it a reference to our hofHeader function
  • Line 19 this is our template function
  • Lines 20-54 is a massive string to define our style block
  • Line 55 this is our actual HTML. We begin with a wrapper div (that we don’t close in this template) with a class of halloffame since this allows us to target our CSS classes very specifically. We also provide a div to hold our title text
  • Line 56 we return the combined styles and header strings

When you refresh the page now you will get this monstrosity:

lve01

Example Item

We’d like to show each of the items as a box showing the Title and the picture of the Person field. Additionally, we’d like to color the box based on the value of the Color field. When someone clicks on a box, they should be taken to the user’s profile page.

    var HallOfFame = {
        BaseViewID: 1,
        ListTemplateType: 100,
        OnPreRender: hofPreRender,
        Templates: {
            Header: hofHeader,
            Item: hofItem
        },
        OnPostRender: hofPostRender
    };

    function hofItem(ctx){
        var sip = ctx.CurrentItem.Person[0].sip;
        var photoUrl = '/_layouts/15/images/PersonPlaceholder.96x96x32.png';
        if(ctx.CurrentItem.Person[0].picture){
            photoUrl = ctx.CurrentItem.Person[0].picture;
        }
        var photo = '/_layouts/15/userphoto.aspx?accountname=' + sip + '&size=L&url=' + photoUrl;
        var userUrl = _spPageContextInfo.siteServerRelativeUrl + '/_layouts/15/userdisp.aspx?ID=' + ctx.CurrentItem.Person[0].id;                           

        var item = '<a href="' + userUrl + '">' +
                   '
<div class="hof-item" style="background-color:' + ctx.CurrentItem.Color + ';">' +
                   '
<div>' + ctx.CurrentItem.Title + '</div>
' +
                   '  <img style="max-height:72px;max-width:72px;" src="' + photo + '"/>' +
                   '</div>
' +
                   '</a>';
        if(ctx.ListData.LastRow == ctx.CurrentItem.ID){
            var closures = '</div>
</div>
';
            return item + closures;
        } else {
            return item;
        }
    }

Here’s what we just did:

  • Line 9 we added an Item property to our Templates object and gave it a reference to our hofItem function
  • Line 60 this is our template function
  • Lines 61-66 is the building of the profile picture image.
    • In standard CSR, a person field is returned as an array of objects with additional properties about a person. One of those properties is the picture property which is the URL to the profile picture for a given user.
    • If a user does not have a profile picture, this property is not included
    • So, we ensure that if the picture is not available we use the standard person placeholder image instead
    • Additionally, if your My Site web application is hosted at a different domain (even sub domain) users using machines not joined to the domain (IE iPads) may be prompted to log in for each profile picture you display.
    • So, instead of using the profile picture directly, we use the userphoto.aspx page to provide us the picture without leaving the domain
  • Line 67 builds the URL to the user profile page using the user id property
  • Lines 69-74 builds our actual HTML for each item.
    • We wrap everything in a link to the user profile
    • We use an inline style to set the background-color to the choice field
      • This is not a good idea and is only used as an obvious example, generally you should not directly incorporate values into markup like this. It would be easy to break simply by adding a choice for a color that is not one of the named HTML colors.
    • Line 67 checks if this item is the last item returned
      • In csrShim we could simply check ctx.CurrentItem.lastRow
      • If this is the last item, we close the wrapper divs left open in the header template and return those after the item.
      • This is necessary since the Footer template will be in a different div and leaving open HTML tags can cause display issues
    • If this wasn’t the last item, we just return the basic item box in line 80

When you refresh the page now things are looking truly amazing:

lve02

Example Footer

We’d like to add some instructions to the bottom to remind people to click on these boxes.

    var HallOfFame = {
        BaseViewID: 1,
        ListTemplateType: 100,
        OnPreRender: hofPreRender,
        Templates: {
            Header: hofHeader,
            Item: hofItem,
            Footer: hofFooter
        },
        OnPostRender: hofPostRender
    };

    function hofFooter(ctx){
        var footer = '
<div class="halloffame">
<div class="hof-footer">Click on a person to learn more</div>
</div>
';
        return footer;
    }

Here’s what we just did:

  • Line 10 we added a Footer property to our Templates object and gave it a reference to our hofFooter function
  • Line 84 this is our template function
  • Line 85 builds standalone HTML to be used as our footer. We provide another wrapper with the halloffame class so that our global styles still get applied. Then we give an unnecessary message encouraging clicks.
  • Line 86 we return the footer

When you refresh the page now you will now be struck at the beauty of our creation:

lve03

Continue?

That’s pretty well everything you need to know about client side rendering with list views. However, the final post in the series provides a quick list of best practices to keep in mind while applying CSR to List Views:

Getting Started (List View CSR)

Applies To: SharePoint 2013, 2016, Office 365

List View Client Side Rendering Primer: 3 of 5

About This Series

This series provides a brief overview of Client Side Rendering for List Views (often referred to as JSLink). Basic extension points and examples are included. The goal of this series is to get developers unfamiliar with this programming model quickly up to speed.

How to Get the Code

The example created using the steps in this series can be found in full as part of the csrShim repository:
https://github.com/thechriskent/csrShim/tree/master/Examples/CSR%20Primer

The full project can be found on GitHub:
https://github.com/thechriskent/csrShim

You can download or clone the repository directly from that site. You can also view the code directly on the site. csrShim is not used in this example and is not required.

In the following examples, we will be targeting on premise SharePoint 2013 using SharePoint Designer 2013.

List Setup

For this example, we will be using a custom list with just 3 columns:

  • Title (Text)
  • Person (User)
  • Color (Choice)

To create the list:

  1. From a site where you have full control or design permissions, using the Site Actions menu (gear) choose Add an App
  2. Choose Custom List
  3. Name the list Hall of Fame and click Create
  4. From Site Contents, hover over the Hall of Fame icon and click the ellipsis to show the panel and choose Settings:
    gs01
  5. Under Columns, click Create column
  6. Give a name of Person and set the type to Person or Group then click OK:
    gs02
  7. Under Columns, click Create column again
  8. Give a name of Color and set the type to Choice (menu to choose from). Replace the choices with Tomato, YellowGreen, DeepSkyBlue, Gold (each on separate lines):
    gs03
  9. Navigate to the Hall of Fame list and add several entries:
    gs04
  10. On a page, choose to Edit and click the Add a Web Part button
  11. Under Apps, choose Hall of Fame
  12. Click Stop Editing

By default, you have a very OOTB view of the list as a table with column headers and not a lot of excitement. So, let’s customize it!

Script Setup

To use the JS Link property of the XSLTListView web part we added in the steps above (step 11), we’ll need a JavaScript file to point to.

  1. Open your site in SharePoint Designer
  2. Using the All Files navigation option, open a Document Library (generally, the Style Library is a great location for these types of resources)
  3. In the File dropdown choose JavaScript:
    gs05
  4. Name the file js
  5. Return to the page (in the browser) where you added the web part
  6. Edit the page and using the dropdown on the web part choose Edit Web Part
  7. In the web part tool pane, expand the Miscellaneous section
  8. In the JS Link box, Provide the path (You can use SPURLs) to the JavaScript file we created earlier then click OK:
    gs06
  9. Nothing will happen (we haven’t provided any code yet). Click Stop Editing on the page
  10. Return to SharePoint Designer and Right-Click on the HallOfFame.js file and choose Edit File in Advanced Mode
  11. Enter the code from the CSR Boilerplate Code below

CSR Boilerplate Code

(function(){

    var HallOfFame = {
        BaseViewID: 1,
        ListTemplateType: 100
    };

    SP.SOD.executeFunc('clienttemplates.js','SPClientTemplates',function(){
        SPClientTemplates.TemplateManager.RegisterTemplateOverrides(HallOfFame);
    });

})()

The above code is simply boilerplate for the extension points we will fill in below. Here is what this code currently does:

  • Line 1 is the declaration of an immediately invoked function expression (IFFE) that will ensure our code will not be placed in the global namespace. This convention should always be used with JS Link files.
  • Lines 3-6 is the beginning of our template object. Soon we will be adding more properties to help determine how the list is formatted. For now, we’ve specified the BaseViewID and the ListTemplateType which is how we target the Custom List (type 100) so that SharePoint knows what data to provide us.
  • Line 8 uses a Script On Demand (SOD) method to only call our function once the clienttemplates.js file has loaded and the SPClientTemplates object is ready to be used. This is essential to prevent our script from executing before SharePoint is ready.
  • Line 9 registers our template object with the SharePoint CSR engine

Refreshing at this point will still not apply anything since we did not provide any Templates just yet.

Continue?

Everything is in place for us to provide some actual CSR templates, but in order to do so we need to understand what templates are even available and how to use them. So, check out the next post in this series:

jsMessage Basic Example

Applies To: Construct 2, jQuery, jsMessage

In my last post, Introducing jsMessage for Construct 2, I gave a brief overview of my C2 Plugin jsMessage. jsMessage enables sending and receiving messages in Construct 2 through jQuery events. You can read more in that post, but the basic idea is the ability to communicate to a Construct 2 game through the Browser. The license is free for everybody and attribution isn’t required.

In this post, I’m going to walk through the jsMessageTest Basic game to show you exactly how it works. You can also download it over on CodePlex if you’d like to follow along.

“Game” Overview

When you first run the project not much is going to happen. You’ll see a big red message that says “Nothing Yet…” – once you’ve successfully sent a message the contents will be displayed here.

InitialScreen

This is a very simple game project with just a few assets and only a small set of standard objects (Touch, Sprite, Browser, Particles, Text and Text Box). The only custom object is the jsMessage object. This was added to the project like any other object and can be found in the Web section (assuming you’ve installed it):

jsMessageInsertNewObject

Receiving Messages in Construct 2

The Event Sheet

jsMessage provides 2 conditions for receiving messages. The first one, Message Received, fires every time a message is received. In our game we are using it to set the text of the txtRecevied object (the big red text). We are also outputting additional information to the console. This helps illustrate several of jsMessage’s expressions but is not something you’d normally do outside of debugging. Here’s what this section of the Event Sheet looks like:

EventSheet-MessageReceived

Using the Log in console action of the built-in Browser object, we output information about the received message. The jsMessage.MessageRaw expression provides the full message string. The jsMessage.Command expression provides only the first part of the message before any values (known as the command). The jsMessage.ValueCount provides the total count of values passed (additional strings after the command separated by the Value Separator Property).

Finally, there’s a For Loop that outputs each of the message’s values (if there were any) by using the jsMessage.Value() expression.

Client-Side

Let’s give it a go. For all of the client-side examples we’re just going to type the jQuery commands directly into the console. So go ahead and run the project and open the Dev Tools (Just hit F-12 in Chrome) and switch to the console. Type the following:

$(document).trigger('CKjsMessageSend','Hello World');

Once you hit enter, your screen should look like this:

HelloWorld

You can see the txtReceived object had its text set to the message and the console has all the log messages we expected (Raw & Command are equal in this case and the Value Count is 0).

Now try sending this:

$(document).trigger('CKjsMessageSend','DoThing|Turds|Sunshine');

DoThing

You’ll see the txtReceived object gets the full message just like before, but if you look in the console you’ll see some differences. We can now see our Command is DoThing and that we have 2 values: Value 0 is Turds and Value 1 is Sunshine. Of course, we are assuming the use of the default separator (The separator is customizable so it’s always a good idea to use the Separator Events to determine what that is before sending/receiving messages on the client).

Responding to Commands in Construct 2

The Event Sheet

Another condition provided by jsMessage is Command Received. This condition lets you specify the command to listen for. This is what we’re doing in the Turtle section of the Event Sheet:

TurtleCommand

We are listening for the command, “Turtle”. When it’s received we move a turtle sprite across the screen using a Bullet behavior (There are also a couple of conditions to reset the turtle once it leaves the screen).

Client-Side

Here’s how we trigger this command from the console:

$(document).trigger('CKjsMessageSend','Turtle');

Turtle

Look at that cute turtle! LOOK AT IT!

You can see everything works just like any standard condition (Note that the console also provides us all the extra information because both the Command “Turtle” Received and Message Received conditions are firing).

Responding to Commands with Values in Construct 2

The Event Sheet

Commands are just messages which means they can also have attached values. This can be seen in the Explosions section of the Event Sheet:

ExplosionsCommand

We are listening for the command, “Explode”, but we’ve added some additional conditions to ensure that there is an included value (jsMessage.ValueCount = 1) and that that value is an integer greater than 0 (int(jsMessage.Value(0)) > 0).

Once the above conditions are met,we use a For Loop to create the number of explosions (particle objects) as specified by the passed value (with a max of 10 cause let’s not get crazy!).

Client-Side

Here’s how we trigger this command from the console:

$(document).trigger('CKjsMessageSend','Explode|7');

Explode

GLORIOUS EXPLOSIONS!

Sending Messages from Construct 2

The Event Sheet

jsMessage provides a single Action, Send Message, that is really easy to use. You just provide the message as a parameter and it’ll take care of it:

SendEventSheet

All we’re doing above is treating the icon sprite like a button by responding to a tap (or click). We flash the button to give some feedback to the user that they tapped it and then call the Send Message action with the text of the txtboxSend object. Of course, nothing is going to happen if nobody is listening on the other side…

Client-Side

To receive messages from Construct 2 you will need to register to respond to the appropriate jQuery event. Here’s an extremely simple response that just writes the sent message out to the console:

$(document).on('CKjsMessageReceive',function(e,m){console.log(m);});

To test, just write something in the text box and click the button:

SendMessage

 

Sending Messages with Values from Construct 2

The Event Sheet

To send messages with values from your game you’ll build your messages using the same format as above. You can see an example of this in the Responding to Requests section of the Event Sheet:

RespondingToRequests

 

When we get the command, “FPS?”, we use the Send Message action to send a custom message built by concatenating a command, “FPS”, the separator using the jsMessage.Separator expression and the C2 value, fps. We could have just typed the default separator but since this is a customizable property it’s always better to use the jsMessage.Separator expression.

Client-Side

To test this one, we need a slightly more elaborate response function:

$(document).on('CKjsMessageReceive',function(e,m){console.log('Client-Side Message Received!');var parts=m.split('|');console.log('Command Received: '+parts[0]);for(var v=1; v<parts.length;v++){console.log('Value Received: '+parts[v]);}});

I’ve kept it to one line above so that it can be easily pasted in the console, but here’s what it looks like where it’s a little more readable:

$(document).on('CKjsMessageReceive',function(e,m){
    console.log('Client-Side Message Received!');
    var parts = m.split('|');
    console.log('Command Received: ' + parts[0]);
    for(var v = 1; v < parts.length; v++){
        console.log('Value Received: ' + parts[v]);
    }
});

SendMessagesWithValuesWhen we send the “FPS?” message to the game it responds with the fps information. Our client-side response just outputs the C2 message right back to the console. I’ve hard-coded the default separator in this example, but you’ll want to use the Separator Events beforehand to ensure you know what the separator is before receiving/sending messages from the client.

 

Be sure to check out the full documentation for more details. Stay tuned for an upcoming post where I’ll show you an actual use case for this plugin. WOWEE!

jQuery OuterHTML

Applies To: jQuery 1.0+

Often in debugging I’d like to pull back the full HTML for an object and take a look at it. The html() method is great for a lot of things, but it’s limited to just the inner HTML and often doesn’t contain the stuff I’m looking for. So here’s a helper function I wrote that gives you back the full HTML as a string:

function fullHTML(jQueryElem) {
	var fullH = jQueryElem.wrap('<div>').parent().html();
	jQueryElem.unwrap();
	return fullH;
}

All it does is perform a temporary wrap around whatever jQuery Element you pass in, grab it’s parent (which is now the Div we just wrapped it in) and grab the parent’s inner HTML (which is of course the full HTML of our object). Then we just unwrap it in line 3 and return the string in line 4.

You can call it like this (this code just writes it out to the console):

console.log(fullHTML($('#myobjectID')));

It’s quick and easy and I use it in development all the time. Hopefully it helps you too.

Use SPServices in Nintex Forms 2010

Applies to: SharePoint 2010, SPServices, Nintex Forms 2010

Marc D Anderson’s SPServices (jQuery for SharePoint Web Services) can save you a ton of time and make your SharePoint sites more responsive and dynamic with very little effort. Nintex can really beef up your workflows and/or your forms. So it makes a lot of sense to bring these together!

Recently I was tasked with pulling some user profile information to display on a Nintex Form that was being used to launch a site workflow. Unfortunately, there is no way to run workflow code before showing the initial form; so all of those great Nintex Workflow Actions were unavailable to me. Fortunately, SPServices allows easy querying of the User Profile Service directly from client side script.

Nintex Forms uses a copy of the jQuery library that you can access through NWF$. So just adding SPServices to your form’s page won’t work, but since jQuery is already there it’s fairly simple to get it all hooked up.

The first thing to do is to get the appropriate version of SPServices. The version of SPServices you use depends on the version of jQuery in use. To figure out what version of jQuery Nintex Forms is using you’ll need to temporarily add some custom JavaScript to your form.

To add JavaScript to Nintex Forms, from the Form editor click the Settings button in the Ribbon and then expand the Custom JavaScript section at the bottom:

FormJavascript

You can take advantage of NWF$ here. Simply add the following to the box and click Save:

console.log(NWF$().jquery);

Open the F12 Developer tools and preview your form. In the Console you should see the version of jQuery being used by Nintex Forms. For us it was 1.6.1. So we should be fine using the latest version of SPServices.

You’ll need to download the SPServices library. Since SPServices uses an IIFE to extend the jQuery object, we’ll need to make a slight adjustment to instead extend the NWF$ object. This is actually really easy. Open up the minified version of SPServices (the one with the .min at the end of the file name) and go all the way to the very end where you’ll see (jQuery);

Just replace the jQuery portion with NWF$ and save with a different name (maybe put an NF.min.js on the end). It’ll look something like this:

SPServicesCode

Then just upload it somewhere in your site collection (The Style Library is generally the best place).

To reference that file go back to the Nintex Form editor and click that Settings button again and expand the Advanced section. Scroll down to the Custom JavaScript Includes section and add the address of the script you just uploaded (If you have multiple JavaScript Includes you just put one on each line):

SPServicesInclude

Press Save and that’s it! You can now access SPServices to do whatever fancy magic you need!

To make sure it worked, scroll back down to that Custom JavaScript section of the Form Settings dialog and add the following code:

NWF$(document).ready(function(){
     console.log(NWF$().SPServices.Version());
});

Then just make sure those F12 developer tools are open and preview the form again. You’ll see the version of SPServices logged out to the console.

DevConnections 2012

Applies To: SharePoint, SQL, .NET, HTML5

I just got back from DevConnections 2012 in Las Vegas, Nevada. I learned several things that the made the trip worthwhile and I’m glad I got to be a part of it. I choose DevConnections over SPC because I wanted to take workshops from a variety of tracks including SQL, .NET and HTML5. Choosing DevConnections also meant I didn’t have to go alone.

There were several good speakers and I received plenty of swag (8+ T-Shirts, an RC helicopter, a book and more). Not surprisingly, I enjoyed the SharePoint Connections track the most and Dan Holme was my favorite speaker.

For all those that didn’t get to go I thought I’d share my notes from the sessions I attended and any insights I gained as well. My notes are not a total reflection of what was said, but represent things I found interesting or useful.


Where Does SharePoint Designer 2010 fit in to Your SharePoint Application Development Process?

Asif RehmaniSharePoint-Videos.com

My Notes:

  • Always use browser first when possible
  • Anything Enterprise wide should be VS or buy
  • DVWP also called Data Form WP
  • DVWP multiple sources to XML with XSLT
  • LVWP specific to SP lists
  • Browser limits in views don’t apply in Designer (Grouping, Sorting, etc.)
  • Import Spreadsheet is only through browser – NOT SP Designer
  • Conditional Formatting in views including icons (put all icons in cell and change content conditional formatting on each).Pictures automatically go in SiteAssets
    • Sometimes img uploads retain local path (switch to code and correct src url)
  • Formulas: select parameter and double click the function to have the selection become the 1st parameter
  • DVWP can easily be changed to do updates: switch from text to text box, then add a row and insert a form action button and choose the commit action (save)
  • Parameters for all sorts of stuff (username, query string, etc)can be used all over including in conditional formatting
  • SPD was designed and intended to be used in production – not a lot of support for working in Dev and moving to Production
    • WP can be packaged (DVWP) for import elsewhere
    • Reusable workflows can also be packaged

Key Insights:

  • SharePoint Designer is fine to be used in production (and in fact requires it in certain cases). However, there are things you can do to minimize the amount of work done in production.
  • SP Designer is pretty powerful and can replace a lot of extra VS development

Overall Impression:

Just as in his videos, Asif was a great presenter. He was very personable and knowledgeable. The session ended up being less about when SP Designer should be used in your environment and more a broad demo of what can be done with Designer. This was a little disappointing but I learned enough tips and tricks that I really didn’t mind too much. Interestingly, some people in the audience asked about an intermittent error they’ve been receiving in SP 2010 for some Web Parts they’d applied conditional formatting too. This was almost certainly the XSLT Timeout issue and I was able to provide them a solution.


Data Visualization: WPF, Silverlight & WinRT

Tim HuckabyInterknowlogy

My Notes:

  • WPF is great at 3D, cool demo of scripps molecule viewer (codeplex)
  • Silverlight is dead
  • Winforms is dead
  • HTML 5 hysteria is in full swing
  • HTML 5 has a canvas and SVG support
  • ComponentOne has neat HTML 5 sales dashboard demo

Key Insights:

  • Silverlight has lost to HTML 5 and we shouldn’t expect another version.

Overall Impression:

This was obviously a recycled workshop from several years ago (he actually said so) that he added a couple of slides to. In his defense, he planned to show a WinRT demo but the Bellagio AV guys were unable to get the display working. Regardless it seemed more like a bragging session. He showed pictures of him with top Microsoft people, showed his company being featured in Gray’s Anatomy, and alluded to all the cool things he’s involved with that he couldn’t mention.

This was pretty disappointing. I am already aware that .NET can do some pretty awesome things including some neat visualizations. I was hoping to get some actual guidance on getting started. Instead I got Microsoft propaganda from 3 years ago about why .NET (specifically WPF) is awesome. Tim Huckaby is obviously a very smart guy and has a lot of insight to share. Hopefully I’ll be able to attend a workshop from him in the future on a topic he cares a little more about.


Building Custom Applications (mashups) on the SharePoint Platform

Todd Baginski – http://toddbaginski.com/blog/

My Notes:

  • Used Silverlight but recommends HTML 5
  • Suggests that all mashups should be Sandbox compatible
  • Bing Maps has great examples requiring little work
  • SL to SL: localmessagesender use SendAsync method. In receiver setup allowedSenderDomains list of strings. Use localmessagereceiver and messagereceived event. Be sure to call the listen() method!!
  • Assets Library great for videos
  • Silverlight video player included in SP 2010/2013. 2013 has an additional fallback HTML 5 player.
  • External Data Column: Works as lookup for BCS
  • OOTB \14\TEMPLATE\LAYOUT\MediaPlayer.js: _spBodyOnLoadFunctionNames.push(‘mediaPlayer.createOverlayPlayer’); after you’ve made links hook ‘em up: mediaPlayer.attachToMediaLinks((document.getElementById(‘idofdivholdinglinks’)), [‘wmv’,’mp3′]);
  • OOTB \14\TEMPLATE\LAYOUT\Ratings.js: ExecuteDelayUntilScriptLoaded(RatingsManagerLoader, ‘ratings.js’); RatingsManagerLoader is huge, see slides. Then loop through everything you want to attach a rating to.
  • SL JS call: HtmlPage.Window.Invoke(“Jsfunctionname”, new string[] { parameter1, parameter2})
  • JQuery twitter plugin
  • SP 2013 has geolocation fields. Requires some setup & code. He has app to add GL column & map view to existing lists.
  • Even in SP 2013 the supported video formats are really limited
  • AppParts are really just iframes.  Connections work different. Not designed to communicate outside of app.

Key Insights:

  • Silverlight to Silverlight communication is pretty simple but will be pretty irrelevant in SharePoint 2013
  • Getting the Video Player to show your videos when using custom XSLT takes some work
  • Adding a working Ratings Control when using custom XSLT is even more complicated and convoluted
  • New GeoLocation columns in SP 2013 will be really cool, but adding them to existing lists is going to be a pain.

Overall Impression:

Todd had a lot of good information and you could tell he knew his stuff. Unfortunately he has a very dry style. Regardless, I enjoy demos that show actual architecture and code and there was plenty of that.

I do wish he’d updated his demos to use HTML 5 as he recommends. It’s very frustrating to hear a presenter recommend something different and then to spend an hour diving into the non-recommended solution. Additionally, although I prefer specific examples (and his were very good) I prefer to have more general best practices/recommendations presented as well. But despite all that he gave a few key tips that I will be using immediately and that is the primary thing I’m looking for in a technical workshop.


Creating Mobile-Enabled SharePoint Web Sites and Mobile Applications that Integrate with SharePoint

Todd Baginski – http://toddbaginski.com/blog/

My Notes:

  • AirServer $14.99 shows iPad on computer
  • Mobile is much better in SP 2013
  • Device channel panel allows content to target specific devices

Key Insights:

  • Mobile is important. You can struggle with SP 2010, but you should probably just upgrade to SP 2013

Overall Impression:

I enjoyed Todd’s other session (see above), but this one was too focused on SP 2013 to have any real practical value for me.


Getting Smarter about .NET

Kathleen Dollard – http://msmvps.com/blogs/kathleen/

My Notes:

  • Lambdas create pointers to a function
  • LINQ creates expressions that can be evaluated everywhere
  • int + int will still be an int even if larger than an int can be. No errors, but addition will be wrong. (default in C#, VB.NET will break for default)
  • There is no performance gain by using int16 over int32, some memory is saved but is only significant when processing multimillion values at the same time
  • VisualStudio 2012 will be going to quarterly updates
  • Static values are shared with all instances – Even among derived classes!
  • LINQ queries Count() does full query
  • Func last parameter is what is returned
  • Closure is the actual variable in a lambda (not copy) so multiple lambdas can be changing the same variable
  • Projects can be opened in both VS 2010 and VS 2012 at the same time

Key Insights:

  • Despite all the new and exciting things that keep getting added to .NET, a firm grip of the basics is what will really make a difference in your code and ability to make great applications
  • Static sharing even among derived classes makes for some potential mistakes, but also for some very powerful architecture
  • LINQ and Lambdas are some crazy cool stuff that I should stop ignoring
  • .NET is very consistent and following it’s logic rather than our own assumptions is key for truly understanding what your code is doing

Overall Impression:

I really enjoyed this session. It was the most challenging workshop I attended despite it’s focus of dealing with things at the most basic level. Kathleen kept it fun (although she could be a little intimidating) and continued to surprise everyone in the room both with the power of .NET and the dangers of our own misconceptions. She pointed out several gotcha areas and provided the reasoning behind them. This was a last minute session, but it was also one of the best.


Wish I’d Have Known That Sooner! SharePoint Insanity Demystified

Dan Holme – Intelliem

My Notes:

  • SQL alias: use a fake name for SQL server to account for server changes/moves. Use CLICONFIG.exe on each SP server in the farm. Do NOT use DNS for this (CNAMEs). Consider using tiers of aliases for future splitting of DBs: content, search, services – all start with the same target and changed as needed
  • ContentDB sizing: change initial size and growth. Defaults are 50mb and 1mb growth. Makes a BIG difference in performance.
  • ContentDBs can be up to 4 TB. Over 200 GB is not recommended.
  • SiteCollections can be same as ContentDBs but 100 GB is as high with OOTB tools
  • Limit of 60 million items per ContentDBs (each version counts)
  • Remote BLOB storage: SP is unaware. Common performance measurements are mostly inaccurate because they are based on single files. Externalizing all BLOBs is significant performance boost. 25-40%! Storage can be cheaper too but complexity increases. Using a SAN allows you to take advantage of SAN features (ie deduplication – which really reduces storage footprint). RBS OOTB is fine, but you can’t set business rules.
  • Office Web Apps no longer run on SP servers in 2013. These are great, test on SkyDrive consumer.
  • Get office365 preview account
  • Nintex highly recommended over InfoPath. InfoPath is supported but unenhanced in 2013, likely indicator of unannounced strategy.
  • AD RMS allows the cloud to be more secure than on-premise. Allows exported documents to have rights management that restricts actions regardless of location. Very difficult to setup infrastructure. Office365 has this which is compelling reason to migrate.
  • User Profile DB is extremely important and becomes much more so in SP 2013
  • Claims Authentication is apparently a dude pees on a server and then gets shot with lasers:
Pee on a server LASERS!
  • Upgrade to 2013 should be done as quickly as possible. Much easier than 7-10. Fully backward compatible. Both 14 & 15 hives.
  • Governance is very important!

Key Insights:

  • Preparing for growth up front with SQL aliases is a great idea
  • Nintex and Office 365 both need more investigation by me
  • Remote Blob Storage is a good idea for nearly everyone – very different perspective than what I’ve previously been told!

Overall Impression:

This session was full of great tips and best practice suggestions tempered with practical applications. This was exactly the kind of information I came to hear. Dan did a great job of presenting a lot of information (despite a massive drive failure just previous to the convention) while keeping it interesting. The only thing that was probably a little much was his in-depth explanation of Claims Authentication. His drawings were pretty rough and his enthusiasm for the topic didn’t really transfer to the audience. Regardless, this was a great session.


SharePoint Data Access Shootout

Scot Hillier – http://www.shillier.com

My Notes:

  • LINQ cannot query across multiple lists (unless there is a lookup connection)
  • SPSiteDataQuery can query all lists of a certain template using CAML within a Site or Site Collection
  • SPMetal.exe generates Object Relational Map needed for LINQ  (in hive bin)
  • LINQ isn’t going to have much support in SP 2013
  • SP 2013 has continuous crawl
  • Keyword Queries are very helpful
  • KQL: ContentClass determines the kind of results you get. (ie STS_Web, STS_Site, STS_ListItem_Events)
  • Search in 2013 provides a rest interface to use KQL in JavaScript
  • CSOM is very similar to Serverside OM
  • CSOM is JS or .NET

Key Insights:

  • Keyword Query Language (KQL) needs more consideration as an effective query language for SharePoint.
  • LINQ isn’t actually a great way to access SP data despite Microsoft’s big push over the past couple of years.

Overall Impression:

This was a strange session. He didn’t go into enough depth about any one data access method to provide any real insight to those of us familiar with them and he moved so quick that anyone new to them would just have been overwhelmed. This session would have been better if he’d given clear and practical advise on when to use these methods rather than just demoing them. My guess is that he was trying to cover way too much information in too little time. However, I did enjoy hearing more about the Keyword Query Language since this is something I haven’t done much of and is rarely mentioned. Those tips alone made the whole session worthwhile.


HTML5 JavaScript APIs: The Good, The Bad, The Ugly

Christian Wenz – http://www.hauser-wenz.de/s9y/

My Notes:

  • HTML5 is a large umbrella of technologies
  • Suggests Microsoft WebMatrix is a good Editor
  • Semantics for HTML elements is a powerful new feature: input type = email, number, range, date, time, month, week, etc. These customize the type of editor and adds validation
  • Suggests Opera Mobile Emulator is a good testing tool
  • Additional elements: aside, footer
  • Requesting location: navigator.geolocation.getCurrentPosition(function(result){console.log(result)});
  • to debug local cache use Google Chrome by going to: Chrome://app cache-internals
  • Worker() web worker allows messaging for functions
  • CORS allows cross domain requests
  • Web sockets do not have full support yet but will be very cool

Key Insights:

  • HTML 5 is going to dramatically change how we think of website capabilities – eventually.
  • Although exciting, HTML 5 has a long way to go and several of it’s most compelling features have little to no support in main stream browsers.

Overall Impression:

Christian did a good job of keeping the energy up about HTML 5 and showing off some of the cool features. Unfortunately he seemed to lose site of the big picture in favor of really detailed samples. I enjoyed the presentation but would like to have had more guidance about how to get started and what to focus on with this new style of web development.


Roadmap: From HTML to HTML 5

Paul D. Sheriff – http://weblogs.asp.net/psheriff/

My Notes:

  • Lots of new elements – but they don’t do anything. They make applying CSS easier and allow search engines to parse through a page easier.
  • Browsers that don’t understand new elements will treat them as divs – but styles won’t be applied.
  • Lots of new input types (color, tel, search, URL, email, number, range, date, date-time, time, week, month)
  • New attributes (autofocus, required, placeholder, form validate, min, max, step, pattern, title, disabled)
  • CSS3 has huge style upgrades but there are still a lot of browser incompatibilities
  • JqueryUI, Modernizr = good tools, use VS 2012 with IE10 or Opera
  • Modernizr allows you to use HTML 5 with automatic replacements in incompatible browsers. Uses JQuery and is included automatically with VS 2012.
  • This stuff is not ready for the prime time except for mobile browsers. Modernizr fills in those gaps.
  • Box-sizing can be either border-box or content-box, which helps with the div width interpretation problem
  • Dude appears to hate JavaScript and HTML 5, sure love hearing a presenter complain about what we all came to learn about!

Key Insights:

  • Use Visual Studio 2012 with Modernizr to make HTML 5 websites

Overall Impression:

This session really annoyed me. Paul was very knowledgeable and had a lot of information to share. Unfortunately, he was so busy bashing the technology we all came to see that it was hard to know why we were even there.


Scaling Document Management in the Enterprise: Document Libraries and Beyond

Dan Holme – Intelliem

My Notes:

  • Can store up to 50 million documents in a single document library
  • SP 2013 allows documents to be dragged onto the doc library in the browser to upload – no ActiveX required
  • When a User is a member of the default group for a site they get the links in office and on their mysite. Site Members is the default group by default, but this can be switched in the group settings. Suggests creating an additional group that contains everyone on the site and has no permissions, then this can be the default group.
  • Email enabled document libraries can be very helpful for receiving documents outside of your network
  • Pingar is a recommended product he briefly mentioned
  • Big improvements in navigation using managed metadata service in SP 2013
  • Content type templates can use the columns as quick parts in Word

Key Insights:

  • Separating Site Membership from Site Permissions by creating an additional group just for managing memberships is a great idea.
  • A lot can be done with SP 2010 but SP 2013 will add a few key features to make things easier (drag and drop on the browser will be awesome).

Overall Impression:

The tip about site membership was worth the whole session. Additionally he reviewed a lot of the basics of content types and the content type hub. While this wasn’t particularly helpful to me, I can’t wait to get ahold of his slides for both this and his other sessions. He had way too many slides for the amount of time he was given.

This session reminded me of how powerful SharePoint is at so many things. Document management is not a particularly exciting topic to me but it is one of the key reasons we are using the SharePoint platform. A review of the features available to maintain the integrity of our data and to simply the classification of that data was very helpful.


SharePoint in Action: What We Did at NBC Olympics

Dan Holme – Intelliem

My Notes:

  • Keep SharePoint simple. Use OOTB features as much as possible
  • 300 hours of content broadcasted per day
  • NBCOlympics.com streamed every competition live
  • Most watched event in TV history
  • 3,700 NBC Olympics team members
    • 1 SP admin/support
  • PDF viewing was turned on despite security concerns
  • Set as default IE page – very difficult to do, they used a script to set a registry entry to account for multiple OSs and browser versions
  • All additional web applications were exposed through SP using a PageViewer WP
    • Phone Directory, Calendar application
  • WebDAV was used to allow other apps to publish documents
  • Global Navigation on top site using tabs (drop down menus)
    • Quick launch had contextual items to site
    • Navigation centric homepage, kept navigation as simple as possible. Only homepage had global navigation.
  • No real branding (put picture on right and used a custom icon). They set the site icon to go to the main web page because it’s what users expected.
  • Did not use lists or libraries as terms instead used Content (libraries, other lists) and Apps (Calendar, Tasks, etc.)
  • Suggests hiding “I like it” and notes since they are not helpful and deprecated in SP 2013
  • Site mailboxes for teams using OWA
  • Embedded documentation: Put basic instructions right on the homepage of team sites as needed. Also above some document libraries – basic upload and open instructions.
  • Focus on usability since there was no time for training
  • Took out All Site Content link and all other navigation was on homepage of site. Sub sites had tab links to parent site.
  • Used InfoPath to customize List Forms mostly to add instructions (placed below field title on left)
  • Lots of calendars. Conference room calendars were very popular. Didn’t use exchange for this in order to accommodate outside users.
  • Used InfoPath lists and workflows to replace paper processes. Kept them simple but effective. Although not used in this case, he recommends Nintex for more advanced needs.
  • Self-service help desk: printer/app installs, FAQs. Showed faces of team.
    • PageViewer web part and point it to \\servername to show all printers and put instructions on right to get those installed directly out of SP
    • Kept running FAQ to show common solutions
  • IT Administration site: ticket system used issue tracking list highly customized with InfoPath list forms.
    • Inventory lists in IT admin, also DHCP lease reports using powershell to dump that information.
    • List for user requests. WF for approvals, then Powershell took care of approved memberships directly in AD.
    • Used a list for password resets. Powershell would set password to generic password as requested (scheduled task every 5 min)
    • Powershell script to create team sites through list requests
  • SP will never be used to broadcast the Olympics but very effective to manage those teams
  • You must understand your users and build to what users really want/need
    • Don’t overwork, don’t over brand – It just clutters.
    • Don’t over deliver or over train.

Key Insights:

  • Keeping global navigation centralized in a single location (without including everything) and providing contextual navigation as needed can keep things simple from a management perspective while still allowing things to be intuitive.
    • Ensuring all navigation needed is exposed through the Quick Launch eliminates the need for All Site Content (except for Admin) and ensures your sites are laid out well
    • As long as you allow users to easily return to the global navigation from any sub site (Using the site icon is very intuitive) there is no need to clutter every site with complicated menu trees.
  • Request lists and Scheduled Tasks running Powershell Scripts can be used to create easy to manage but very powerful automation.
  • Removal of “I like it” and notes icons is a good idea
  • Embedding instructions directly on a given site/list using OOTB editing tools can increase usability dramatically
  • InfoPath List forms can go a long way towards improving list usability by making things appear more intuitive and providing in line instructions.
  • There are so many cool things in SP it can be easy to forget all the amazing things you can do using simple OOTB functionality. It is far too tempting to over deliver and over share when end users really only want to get their job done in the simplest way possible.

Overall Impression:

This was the best session I attended and made the entire conference worthwhile. It is unfortunately rare that you can see SP solutions in the context of an entire site. Seeing how SP was used to help manage one of the largest and most daring projects I can imagine was both inspiring and reassuring.

Besides the several tips of things they did (many of which will soon be showing up in our environment), he was able to confirm several things we were already doing that we were a little unsure about. Even better was his focus on simplifying things. I get so excited about SP features that sometimes I overuse them or forget the real power of simple lists. It was a fantastic reminder that we are often over delivering and therefore complicating things that just make SP scary and hard to use for end users.


Convention Summary

DevConnections 2012 was great. I had a great time in Vegas and I brought home several insights that have immediate practical value. Really, there’s not much more you can ask for in a technical convention.

Printing the Display View of an InfoPath List Item Form

Applies To: SharePoint 2010

I’ve written previously about a cool feature in SharePoint 2010 Server Enterprise that allows you to customize list item forms using InfoPath. It’s really simple to do and you can get some pretty cool results in just a couple of minutes. For instance, I posted a while back about how to use SharePoint column validation to validate email addresses and phone numbers. Those are still good techniques, but by using an InfoPath list item form it’s just a validation drop down (you can even do regular expressions) and you’re done!

So the ease of validation, conditional hiding of fields, etc. are all pretty useful. However, the thing I like it most for is the ability to use different InfoPath views to match the list item views. So you can have different columns available when you’re editing than when you’re adding a new item, for instance. I especially like to spruce up the Display form.

(For some quick tips on how to get the different views working check out the top of my old post)

So, let’s say you’ve got a nice looking display form. Users open that thing up and decide to print. There’s no button, so they use the print button in the browser. Generally they’ll end up with some mess that prints all of your branding, usually some of the list behind the modal dialog, and if you’re lucky mixed in there somewhere will be your display form. Obviously, that’s not going to cut it.

So I did some digging and found some examples of people using javascript to print the form and their solutions were pretty intriguing. But I didn’t particularly want to have to apply some javascript to every form or to have to add a content editor to the pages, etc. I wanted something that just worked on existing forms and new ones too. So I did a little research into Ribbon customization and came across this great series by Chris O’Brien.

I put it all together in a solution and put it over on CodePlex as WireBear InfoPath Printer. There’s some stuff about it’s license over there (Free for personal and commercial use, etc.) and the basic installation instructions. It’s super easy to setup since it’s just a standard SharePoint Solution that you globally deploy.

You can find the full source code over on CodePlex. It’s not too complex and I’ll probably explain most of it in the next couple of posts. Bottom line is that it adds a Print button to the Ribbon when viewing list items that use an InfoPath Form:

The final printout only shows the form (No Ribbon, No Header, No Footer, No QuickLaunch, etc.).

The button is added using Custom Action XML that is deployed as a feature in the solution. The XML is targeted to allow the button to only be present when Viewing a List Item using an InfoPath Browser based List Form.

When you click the button, standard JavaScript is executed to find the InfoPath div element on the page and to copy the form’s HTML into a new window (along with all standard CSS/script references already present) and uses the browser’s page printing. Once the print dialog closes, so does the window.

We’ve been using it around here for a while and almost no one even knows it’s a custom solution. It looks like part of the UI and it’s use is immediately understood. So, go get it (It’s free!) and let me know what you think.