Using SharePoint and AJAX to Gather Website Feedback
When building intranet web applications, I recommend making it very easy to allow your users to tell you what they think. If you are reading this blog, you know how important instant simple feedback is to web sites, their value, and the trust of their community.
I’ve been using SharePoint lists for this quite a bit.
I often put a “Click
Here to Submit Feedback” link on all the portal pages I
build. However,
about six months ago this
Here is what I did. I used a SharePoint issue list and some
One neat thing about this approach is that it uses SharePoint's built in web services which support POST and GET verbs. When done over an intranet it uses the current user's security context.
In a script block in a web page, set up the following variables:
var
FeedbackHTTP;
var
WSSSiteURL="http://server/site";
//do not terminate with a slash
var
ListName="Sample Issue List"
var
FeedbackTitle="Feedback Sample App";
Now, add a link to your web page to launch the feedback form:
<a
id="linkShowFeedback"
href="javascript:showFeedback();"
class="Action">+ Provide
Feedback</a>
Now we’ll need the showFeedback function and the feedback form. First here is the showFeedback function:
function
showFeedback()
{
//This is a fancy dropshadow effect
sectionFeedback.style.filter="progid:DXImageTransform.Microsoft.DropShadow(OffX=3,
OffY=4, Positive='true', enabled=true,
Color='Gray')";
linkShowFeedback.style.display="none";
//This hides the "Provide Feedback" link while the user
is providing feedback
sectionFeedback.style.display="";
//This shows the feedback form;
sectionFeedback.style.position="absolute";
//Now we need to position the form in the middle of the
screen so that it appears to float over the page.
sectionFeedback.style.posLeft=(document.body.offsetWidth/2)-(sectionFeedback.offsetWidth/2);
if
(document.body.offsetHeight>sectionFeedback.offsetHeight)
{
sectionFeedback.style.posTop=(document.body.offsetHeight/2)-(sectionFeedback.offsetHeight/2);
}
else
{
sectionFeedback.style.posTop=5;
}
frmFeedback.txtFeedback.select(); //Just to speed things up, we select the feedback
textbox so the user can start typing their thought right
away.
}
Now, we’ll need the form. Here is the HTML:
<div
id="sectionFeedback"
class="FeedbackSection"
style="display:none">
<table
border="2"
class="FeedbackSection">
<tr>
<td
align="center"
valign="middle"
class="FeedbackSection">
<form
id="frmFeedback"
onsubmit="return(provideFeedback());"
onreset="return(CancelFeedback())">
Provide a
comment, suggestion, issue, or praise:<br>
<input
type="text"
style="width:100%"
id="txtFeedback"><br>
<input
type="submit"
value="Submit Feedback"
style="FONT-SIZE: 8pt">
<input
type="reset"
value="Cancel"
style="font-size:8pt"
onclick="CancelFeedback()">
</form>
</td>
</tr>
</table>
</div>
Then we’ll need the functions for submitting and cancelling:
function
CancelFeedback()
{
sectionFeedback.style.display="none"
linkShowFeedback.style.display="";
frmFeedback.txtFeedback.value="";
return
false;
}
The function for submitting feedback has the “
function
provideFeedback()
{
var xmldoc;
var batch;
//The batch variable builds up the XML that we are
going to submit to the SharePoint list via the
SharePoint web service
batch =
"<Batch><Method ID='1' Cmd='New'><Field
Name='Description'>"+ frmFeedback.txtFeedback.value +"</Field><Field Name='Title'>"
+ FeedbackTitle +
", Sample 1: " +
SampleForm.txtSample1.value +
", Sample 2: " +
SampleForm.txtSample2.value +
"</Field></Method></Batch> ";
//Make sure you add a "Description" column to the issue
list. It isn't there by default.
xmldoc='<?xml version="1.0"
encoding="utf-8"?><soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><UpdateListItems
xmlns="http://schemas.microsoft.com/sharepoint/soap/">'
xmldoc=xmldoc+'<listName>'+ListName+'</listName>';
xmldoc=xmldoc+'<updates>'
+ batch +'</updates></UpdateListItems></soap:Body></soap:Envelope>';
FeedbackHTTP=new
ActiveXObject("MSXML2.XMLHTTP.3.0");
FeedbackHTTP.Open("POST", WSSSiteURL +
"/_vti_bin/Lists.asmx?op=UpdateListItems", true);
FeedbackHTTP.setRequestHeader("Content-Type","text/xml; charset=utf-8");
FeedbackHTTP.setRequestHeader("SOAPAction","http://schemas.microsoft.com/sharepoint/soap/UpdateListItems");
FeedbackHTTP.onreadystatechange=provideFeedbackResponse;
FeedbackHTTP.Send(xmldoc);
sectionFeedback.style.display="none"
//This hides the feedback form now that the user is
done submitting feedback
linkShowFeedback.style.display=""; //This displays the "Provide feedback" link now that
the user is done submitting feedback
frmFeedback.txtFeedback.value="";
//This clears the feedback text
return
false; //We return false so that the form doesn't attempt to
POST or GET
}
function
provideFeedbackResponse()
{
if
(FeedbackHTTP.readyState==4){
linkShowFeedback.style.display="";
var xmlResponse=new
ActiveXObject("MSXML2.DOMDocument.3.0");
xmlResponse.async=false;
var issueID;
xmlResponse.loadXML(FeedbackHTTP.responseXML.xml);
//This next bit you don't really need, but it is nice
to provide the user with some subtle indication that
their response was submitted.
issueID=xmlResponse.selectSingleNode("/soap:Envelope/soap:Body/UpdateListItemsResponse/UpdateListItemsResult/Results/Result/z:row/@ows_SelectTitle").text;
showstatus("<a target='_blank' href='"+ WSSSiteURL +"/Lists/"
+ ListName +
"/DispForm.aspx?ID=" +
issueID +
"'>Feedback submitted</a>");
}
}
You may notice that there is a showstatus function. That causes a “toast” popup to display with a link to the item the user just submitted.
In this example we also automatically capture some information about the application’s state and include that in the feedback submission. In this way, we can automatically gather some context about the user’s situation without having to ask them to relay that information. In this example, I include it in the description, but you could just as easily break that information out into list columns for sorting, grouping, and analyzing your feedback. This sample doesn’t include any error handling and only works well on intranets. One added bonus is that the user’s authentication goes straight through to the SharePoint list so their username is automatically associated with the feedback item. Here is the whole page’s code:
<!DOCTYPE
html
PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
>
<head>
<title>Feedback Submission Sample - Using SharePoint</title>
<style>
BODY {background:navy}
A.Action {text-decoration:none;color:white;filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=5,
OffY=5, Color='black',
Positive='true',enabled=true)}
A.Action:HOVER {background:white;color:blue}
.FeedbackSection
{color:white}
Table.FeedbackSection
{background:highlight;border-color:indigo}
</style>
<script
language="jscript"
id="FeedbackScript">
var
FeedbackHTTP;
var
WSSSiteURL="http://server/site";
//do not terminate with a slash
var
ListName="Sample Issue List"
var
FeedbackTitle="Feedback Sample App";
function
showFeedback()
{
//This is a fancy dropshadow effect
sectionFeedback.style.filter="progid:DXImageTransform.Microsoft.DropShadow(OffX=3,
OffY=4, Positive='true', enabled=true,
Color='Gray')";
linkShowFeedback.style.display="none";
//This hides the "Provide Feedback" link while the user
is providing feedback
sectionFeedback.style.display="";
//This shows the feedback form;
sectionFeedback.style.position="absolute";
//Now we need to position the form in the middle of the
screen so that it appears to float over the page.
sectionFeedback.style.posLeft=(document.body.offsetWidth/2)-(sectionFeedback.offsetWidth/2);
if
(document.body.offsetHeight>sectionFeedback.offsetHeight)
{
sectionFeedback.style.posTop=(document.body.offsetHeight/2)-(sectionFeedback.offsetHeight/2);
}
else
{
sectionFeedback.style.posTop=5;
}
frmFeedback.txtFeedback.select(); //Just to speed things up, we select the feedback
textbox so the user can start typing their thought right
away.
}
function
provideFeedback()
{
var xmldoc;
var batch;
//The batch variable builds up the XML that we are
going to submit to the SharePoint list via the
SharePoint web service
batch =
"<Batch><Method ID='1' Cmd='New'><Field
Name='Description'>"+ frmFeedback.txtFeedback.value +"</Field><Field Name='Title'>"
+ FeedbackTitle +
", Sample 1: " +
SampleForm.txtSample1.value +
", Sample 2: " +
SampleForm.txtSample2.value +
"</Field></Method></Batch> ";
//Make sure you add a "Description" column to the issue
list. It isn't there by default.
xmldoc='<?xml version="1.0"
encoding="utf-8"?><soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><UpdateListItems
xmlns="http://schemas.microsoft.com/sharepoint/soap/">'
xmldoc=xmldoc+'<listName>'+ListName+'</listName>';
xmldoc=xmldoc+'<updates>'
+ batch +'</updates></UpdateListItems></soap:Body></soap:Envelope>';
FeedbackHTTP=new
ActiveXObject("MSXML2.XMLHTTP.3.0");
FeedbackHTTP.Open("POST", WSSSiteURL +
"/_vti_bin/Lists.asmx?op=UpdateListItems", true);
FeedbackHTTP.setRequestHeader("Content-Type","text/xml; charset=utf-8");
FeedbackHTTP.setRequestHeader("SOAPAction","http://schemas.microsoft.com/sharepoint/soap/UpdateListItems");
FeedbackHTTP.onreadystatechange=provideFeedbackResponse;
FeedbackHTTP.Send(xmldoc);
sectionFeedback.style.display="none"
//This hides the feedback form now that the user is
done submitting feedback
linkShowFeedback.style.display=""; //This displays the "Provide feedback" link now that
the user is done submitting feedback
frmFeedback.txtFeedback.value="";
//This clears the feedback text
return
false; //We return false so that the form doesn't attempt to
POST or GET
}
function
provideFeedbackResponse()
{
if
(FeedbackHTTP.readyState==4){
linkShowFeedback.style.display="";
var xmlResponse=new
ActiveXObject("MSXML2.DOMDocument.3.0");
xmlResponse.async=false;
var issueID;
xmlResponse.loadXML(FeedbackHTTP.responseXML.xml);
//This next bit you don't really need, but it is nice
to provide the user with some subtle indication that
their response was submitted.
issueID=xmlResponse.selectSingleNode("/soap:Envelope/soap:Body/UpdateListItemsResponse/UpdateListItemsResult/Results/Result/z:row/@ows_SelectTitle").text;
showstatus("<a target='_blank' href='"+ WSSSiteURL +"/Lists/"
+ ListName +
"/DispForm.aspx?ID=" +
issueID +
"'>Feedback submitted</a>");
}
}
function
showstatus(message)
{
sectionStatus.style.position="absolute";
sectionStatus.style.posLeft=document.body.clientWidth-sectionStatus.width-10;
if
(document.body.clientHeight>sectionStatus.height)
sectionStatus.style.posTop=document.body.clientHeight -
sectionStatus.height - 5;
else
sectionStatus.style.posTop=document.body.offsetHeight-5;
sectionStatus.filters[0].Apply();
sectionStatusBody.innerHTML=message;
sectionStatus.style.display="";
sectionStatus.filters[0].Play();
window.setTimeout("hidestatus()",4500);
}
function
hidestatus()
{
sectionStatus.filters[1].Apply();
sectionStatus.style.display="none";
sectionStatus.filters[1].Play();
}
function
CancelFeedback()
{
sectionFeedback.style.display="none"
linkShowFeedback.style.display="";
frmFeedback.txtFeedback.value="";
return
false;
}
</script>
</head>
<body>
<table
bgcolor=ActiveCaption><tr><td>
This page demonstrates how to integrate a unobtrusive
user feedback mechanism into your web application that
leverages simple Windows SharePoint Services issue
lists. This
example uses a basic SharePoint issue list and
</td></tr></table>
<table
width="100%">
<tr
height="100%">
<td>
<font
color=Menu>Actions</font><br
/>
<nobr><a
id="linkShowFeedback"
href="javascript:showFeedback();"
class="Action">+ Provide
Feedback</a></nobr>
</td>
<td
bgcolor="Window"
height="100%">
<div
id="sectionFeedback"
class="FeedbackSection"
style="display:none">
<table
border="2"
class="FeedbackSection">
<tr>
<td
align="center"
valign="middle"
class="FeedbackSection">
<form
id="frmFeedback"
onsubmit="return(provideFeedback());"
onreset="return(CancelFeedback())">
Provide a
comment, suggestion, issue, or praise:<br>
<input
type="text"
style="width:100%"
id="txtFeedback"><br>
<input
type="submit"
value="Submit Feedback"
style="FONT-SIZE: 8pt">
<input
type="reset"
value="Cancel"
style="font-size:8pt"
onclick="CancelFeedback()">
</form>
</td>
</tr>
</table>
</div>
<table
id="sectionStatus"
class="StatusSection"
width="100"
height="100"
border="1"
bordercolor="ActiveBorder"
bgcolor="InactiveCaption"
style="display:none;border-style:ridge; filter:
progid:DXImageTransform.Microsoft.Blinds(direction='up',
bands=1,duration=1)
progid:DXImageTransform.Microsoft.Blinds(direction='down',
bands=1);">
<tr>
<td
align="center">
<span
id="sectionStatusBody"
style="FONT-SIZE: 8pt; FONT-FAMILY: Verdana">
</span></td>
</tr>
</table>
<form
id="SampleForm"
name="SampleForm">
Sample Field
1: <input
type=text
id="txtSample1"
name="txtSample1"/>
<br
/>Sample Field 2:
<input
type=text
id="txtSample2"
name="txtSample2"/>
</form>
</td>
</tr>
</table>
</body>
</html>
Note: All code is provided as is with no warranties expressed or implied.