Thursday, December 29, 2005

Extreme SharePoint Design: Adding a Second 'Save and Close' Link to Surveys

Surveys are usually short and to the point but sometimes they can be quite long, especially if there a large number of multiple-choice questions. Users often complain about having to scroll back up to the top of the page to save their responses after filling out the form (this is true of any list, not just surveys). Adding a second 'Save and Close' link to the bottom of the page is an effective solution for improving usability.

Like everything else in SharePoint, implementing this functionality is not quite as simple as it might first appear to be. First off, the pages that display the 'Save and Close' link, newform.aspx and editform.aspx, are just container pages for dynamically-rendered CAML (Collaborative Application Markup Language, that tangle of XML gobbledygook that makes SharePoint go). The real meat is in SCHEMA.XML under /Lists/Voting in the various site definitions (60/Template/1033/SPS and so on). This means that there is no direct HTML for you to edit; you have to modify the proper sections in the schema file.

To begin with, locate the following line:

<Form Type="EditForm" Url="EditForm.aspx" WebPartZoneID="Main">

This is the beginning of the definition section for the Edit Response form on EDITFORM.ASPX. There are four main sections which define the form display: <ListFormOpening>, <ListFormButtons>, <ListFormBody>, and <ListFormClosing>. The HTML for the toolbar that contains the 'Save and Close' link is found in the <ListFormButtons> section. If you haven't worked with CAML before this can be a bit intimidating. It helps to keep the following in mind:

1. It's just XML. Fancy XML, yes, but still XML. All the same rules apply. What's different is the terminology and a whole slew of new tags.

2. All presentation code is encapsulated in <HTML></HTML> tags, and the code itself is encoded within <![CDATA[ blocks. Once the proper tag elements are in place, it's regular old HTML in between.

The first code you will see following the section open tag for <ListFormButtons> is an If...Then expression that checks to see if content approvals are enabled. The <Switch> tag is much the same as an "If" in VB, C# or JavaScript; the <Expr> tag (for 'Expression') is the actual expression and the <Case> tag is the comparison value. The <Default> tag then contains the "Else" value. So, another way of writing this:

<Switch>
<Expr><ListProperty Select="ModeratedList"/></Expr>
<Case Value="0"/>
<Default><HTML><![CDATA[<p>Hellow World!</p>]]></HTML></Default>
</Switch>

Would be this:

function CompareValue()
{
if ModeratedList.Value = 0
{
return;
}
else
{
document.write("<p>Hellow World!</p>");
}
}

For this example, we'll leave the expression in place but take out the messages pertaining to content approvals; since the button at the top of the page will already contain this information, we don't need to repeat it. Removing the redundant code is easy - just strip out everything between the HTML tags so the resulting code looks like the following:

<Switch>
<Expr><ListProperty Select="ModeratedList"/></Expr>
<Case Value="0"/>
<Default><HTML></HTML></Default>
</Switch>

NOTE: Generally, it is a good idea to leave as much of the original code in place as possible. For one thing, you don't know what other elements on the page might be affected (such as nested TD's or DIV's) as it is nearly impossible to read the HTML structure within CAML; for another, a control on the page might rely upon the element to work properly. In this case, the table is stand-alone and there are no ID or NAME tags for a control to reference so the HTML code is obviously discrete and can safely be removed.

Next, we'll copy the toolbar code and append it to the end of the <ListFormBody> section. Cut and paste the following block of code just before the closing </ListFormBody> tag:

<HTML><![CDATA[ <TABLE class="ms-toolbar" style="margin-left: 3px;" cellpadding=2 cellspacing=0 border=0> <TR> <TD class="ms-toolbar"> <table cellpadding=1 cellspacing=0 border=0> <tr> <td class="ms-toolbar" nowrap> <a tabindex=2 ID=diidIOSaveItem class="ms-toolbar" title=]]></HTML><HTML>"Save"</HTML><HTML><![CDATA[ ACCESSKEY=S href="javascript:SubmitForm()" target="_self"> <img src="/_layouts/images/saveitem.gif" ID="tbbuttonstart1" alt=]]></HTML><HTML>"Save"</HTML><HTML><![CDATA[ border=0 width=16 height=16></a></td> <td nowrap> <a tabindex=2 ID=diidIOSaveItem class="ms-toolbar" ACCESSKEY=S href="javascript:SubmitForm()" target="_self"> <LocID ID="L_EditFormSave">]]></HTML> <HTML>Save and Close</HTML> <HTML><![CDATA[</LocID> </a> </td> </tr> </table> </TD> <TD class=ms-separator></td><TD class="ms-toolbar"> <table cellpadding=1 cellspacing=0 border=0> <tr> <td class="ms-toolbar" nowrap> <a tabindex=2 ID=diidIODeleteItem class="ms-toolbar" title=]]></HTML><HTML>"Delete"</HTML><HTML><![CDATA[ ACCESSKEY=X href="javascript:DeleteItem()" target="_self"> <img src="/_layouts/images/delitem.gif" ID="tbbuttonstart1" alt=]]></HTML><HTML>"Delete"</HTML><HTML><![CDATA[ border=0 width=16 height=16></a></td> <td nowrap> <a tabindex=2 ID=diidIODeleteItem class="ms-toolbar" ACCESSKEY=X href="javascript:DeleteItem()" target="_self"> <LocID ID="L_EditFormDelete">]]></HTML><HTML>Delete Item</HTML><HTML><![CDATA[</LocID></a> </td> </tr> </table> </TD> <TD class=ms-separator></td> <TD class="ms-toolbar"><table cellpadding=1 cellspacing=0 border=0><tr><td colspan=2 nowrap><a tabindex="2" class="ms-toolbar" ACCESSKEY=G ID=diidIOGoBack href="]]></HTML><ListProperty Select='DefaultViewUrl'/><HTML><![CDATA[" ONCLICK="javascript:GoBack(']]></HTML><ScriptQuote NotAddingQuote="TRUE"><ListProperty Select="DefaultViewUrl"/></ScriptQuote><HTML><![CDATA[');return false;" target="_self"><LocID ID="L_EditFormGoBack">]]></HTML><HTML>Go Back to Survey</HTML><HTML><![CDATA[</LocID></a></td></tr></table></TD><td width=100%><img src="/_layouts/images/blank.gif" alt=""></td> </TR> </TABLE> <table width=100% border=0 cellpadding=0 cellspacing=2> <tr> <td colspan=5 height=3><img border=0 width=1 height=3 src="/_layouts/images/blank.gif" alt=""></td> </tr> </table><font size=1><br></font>]]></HTML>

Now we need to reformat the toolbar code to get rid of the 'Go back to the survey' link and shorten the length to the size of a normal button. Here is the altered CAML (you might want to cut and paste each block into an editor, restore the formatting and diff the files to see the changes):

<HTML><![CDATA[ <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td width="100%" height="20"></td> </tr> </table> <TABLE class="ms-toolbar" style="margin-left: 3px;" cellpadding=2 cellspacing=0 border=0 width="115"> <TR> <TD class="ms-toolbar"> <table cellpadding=1 cellspacing=0 border=0> <tr> <td class="ms-toolbar" nowrap> <a tabindex=2 ID=diidIOSaveItem class="ms-toolbar" title=]]></HTML><HTML>"Save"</HTML><HTML><![CDATA[ ACCESSKEY=S href="javascript:SubmitForm()" target="_self"> <img src="/_layouts/images/saveitem.gif" ID="tbbuttonstart1" alt=]]></HTML><HTML>"Save"</HTML><HTML><![CDATA[ border=0 width=16 height=16></a></td> <td nowrap> <a tabindex=2 ID=diidIOSaveItem class="ms-toolbar" ACCESSKEY=S href="javascript:SubmitForm()" target="_self"> <LocID ID="L_EditFormSave">]]></HTML> <HTML>Save and Close</HTML> <HTML><![CDATA[</LocID> </a> </td> </tr> </table> </TD> </TABLE> <table width=100% border=0 cellpadding=0 cellspacing=2> <tr> <td colspan=5 height=3><img border=0 width=1 height=3 src="/_layouts/images/blank.gif" alt=""></td> </tr> </table><font size=1><br></font>]]></HTML>

In essence, all we've done is shorten the original toolbar, removed the separator and second link, and added a second table that acts as a spacer (a DIV or clear gif would work just as well). Once finished with EditForm.aspx move on to NewForm.aspx and repeat the process (don't just cut and paste the modified code from EditForm - the two files work a bit differently).

After modifying these two sections, save the file and copy it to the site definition directory on your front-end web server(s). Remember, when altering any of the XML files, you must perform an IISRESET on each FE to see the changes. After resetting, view any survey to see the results. You should have a survey form that looks similar to the following:



This same technique can be used for any list with forms that extend beyond the fold and you can always modify the HTML code apply a different style to the toolbar (like, for example, the wizard buttons in SharePoint) or add javascript drop-downs, mouseovers, etc.