Our company recently had a fundraising drive for a charity. The people on the fundraising committee came up with an idea to raise some money and have a little fun - we would have a trivia contest for employees. You would pay a small fee to enter, with the best score winning a prize, and the proceeds would go to the charity.
I suppose there are web sites out there that can host something like this, but my immediate throught was that we could use SharePoint's survey function to do this. And with a little customization, it can handle the job.
Some of the requirements we had are:
-
You can only take the quiz once.
-
You can't see the questions before you take the quiz.
-
You can't go back and change your answers once you submit them.
-
You only have 10 minutes to take the quiz.
-
Prevent "cheating" as much as possible.
-
Need a way to find the best score (shown in Part 2)
Some of these things are handled out-of-the-box by SharePoint. Others require customizations. Others can't be overcome 100% and require a little trust...
If you know of other ways to do some of these things without customizations, I would love to hear about them.
Requirement: You can only take the quiz once.
This can be handled by setting the survey option "Allow multiple responses?" to No in the survey settings in SharePoint (set it when creating the survey or later using the link "Title, description and navigation" in the Survey Settings.
Requirement: You can't see the questions before you take the quiz.
This one is somewhat difficult to overcome. It depends how secure you want things to be and how savvy your users are.
First, in Survey Settings -> Advanced settings, set the Item-level Permissions to:
Read access: Only their own
Edit access: Only their own
This prevents non-admin users from seeing the responses from any other user who has already responded to the survey. So they won't be able to see the questions using "Show a graphical summary of responses" or "Show all responses" when clicking on the survey. Those two links on the survey's "overview.aspx" page will only show the user's responses, and until the user has taken the survey there will be nothing to show.
However, there is still a hole... On the "overview.aspx" page (which is the 'landing page" you see when you click on the list in "View All Site Content"), there is an option under the Actions menu for "Export to Spreadsheet". That will create an Excel spreadsheet with the survey questions in the headings of the columns even if the user has not responded to the survey yet. The contents of the spreadsheet will be empty, but the questions will be in the column headings. So a savvy user could use this to see the questions prior to taking the survey.
The only way I have found to avoid this is to drop the survey as a web part on a site or web part page, and set the Toolbar Type of the web part to Summary Toolbar and change the Chrome Type to None. Then there will be no toolbar at the top with the Actions method. You need to change the Chrome Type to None, or users will be able to click on the header of the web part and get to the survey's landing page (overview.aspx). Point users to this site/page as the method to take the survey.

This does not entirely solve the problem however. The user can still click on "View All Site Content" and get to the overview.aspx page from there by clicking on the link to it in the Surveys section on the viewlists.aspx page.
One way to solve that is to hide the "View All Site Content" link. You can do that by dropping a Content Editor Web Part (CEWP) on the page and use the Source Editor button to put this in the web part:
<style>
.ms-quicklaunchheader { display: none }
</style>
Change the Chrome Type of the CEWP to none and save it. The "View All Site Content" link will now be gone! (When you need to get to it, you need to add "_layouts/viewlsts.aspx" to the site's URL.
Of course, if you have users who really know their SharePoint, they will be able to still get to the View All Site Content page (viewlsts.aspx) or the survey's overview.aspx page by finding the URL to the site or survey and changing the URL to navigate to it.
In my company's case, we do not really have any non-admin users who know SharePoint well enough to do this, so hiding the "View All Site Content" link is sufficient for us. But if you do have advanced SharePoint users, my recommendation is to rename overview.aspx for the survey to another name, like "overview_admin.aspx" using SPD. Then if you wish, create a new overview.aspx page with nothing on it or some kind of message for anyone navigating to it. Then the only way a user could get to the survey's original overview.aspx page is if the user knew the new name you gave it. (You also need to remember it because you will need it to get the survey results.)
There is yet another way the user would be able to see the answers without taking the survey, and that is to use the Respond link to take the survey and then click the Cancel button after reading the questions. SharePoint doesn't record the response to the survey until the user clicks the Finish button. So we need a way to disable or hide the Cancel buttons (there are two).
This can be accomplished using SPD to edit the survey's NewForm.aspx page, which is the page the user sees when they respond to a survey. Add this Javascript to the page, just after the closing </table> tag after the closing </WebPartPages:WebPartZone> tag:
<script type="text/javascript">
function hideCancel()
{
var e = document.getElementsByTagName("input");
for(var i = 0; i < e.length; i++)
if (e[i].type == 'button' && e[i].value == 'Cancel')
e[i].style.visibility = "hidden";
}
hideCancel();
</script>
This code looks for all input tags that are buttons and have the value (button name) of "Cancel", and sets their visibility to hidden. This will find both Cancel buttons and hide them, so the user now cannot cancel out of the survey once they start it.
There is still one more way the user can see the questions without taking the survey - if the user clicks Respond link to take the survey, then clicks the Back button or closes the browser, SharePoint does not record that the survey was taken. So a user could click Respond, read the questions, click Back or close the browser, do some research, then take the quiz again with their new knowledge.
This can be prevented by submitting the survey results automatically when the "onbeforeunload" event fires in Internet Explorer. Note that this technique may not work with some browsers; some do not support this event. But we are using IE only, so it works in our case.
By submitting the results when "onbeforeunload" fires, clicking the back button or closing the browser will be trapped and the user won't be able to take the survey again. We explain this in the "rules of the game" - we tell them they must finish the quiz once they start it.
Trapping and submitting the results with "onbeforeunload" is tricky. First, the survey form does not have a "submit" button, therefore the form "submit()" function cannot simply be called to submit the results. We need to "click" the Finish button. In order to "click" the Finish button, we need to find it on the page.
Second, "onbeforeunload" will fire any time the page is left, including when the user really clicks the Finish button. If the user navigates off the page without clicking Finish, we want to display a message to let the user know that they screwed up, but we don't want that message to display when they correctly click the Finish button. To do this, we need to insert some Javascript code to disable the onbeforeunload event (set it to null) when the Finish button is really clicked. And there are two Finish buttons on the page, one at the top and one at the bottom. We need to do this to both of them.
This Javascript code will accomplish that.
<script type="text/javascript">
function findFinish(which)
{
var btn = null;
var count = 1;
var e = document.getElementsByTagName("input");
for(var i = 0; i < e.length; i++)
{
if (e[i].type == 'button' && e[i].value == 'Finish')
{
if (count == which)
{
btn = e[i];
break;
}
else
{
count++;
}
}
}
return btn;
}
function insertSubmitEvent()
{
var btn = findFinish(1);
if (btn != null)
{
if (typeof btn.onclick == "function")
{
var oldClick = btn.onclick;
btn.onclick = function() { window.onbeforeunload = null; oldClick(); }
}
}
var btn2 = findFinish(2);
if (btn2 != null)
{
if (typeof btn2.onclick == "function")
{
var oldClick = btn2.onclick;
btn2.onclick = function() { window.onbeforeunload = null; oldClick(); }
}
}
}
function forceSubmit()
{
alert('Since you clicked off the page, your answers will now be recorded - you were warned!');
var btn = findFinish(1);
if (btn != null)
btn.click();
}
insertSubmitEvent();
window.onbeforeunload = forceSubmit;
</script>
This code finds both Finish buttons and inserts code in each button's "click" event to disable the "onbeforeunload" event. This is so a "real" click of the Finish button does not execute our code. We only want our code to execute when the user tries to leave the page in some way other than the Finish button.
Next it sets the window's "onbeforeunload" event to our function, which tells the user that their answers are now going to be recorded since they tried to leave the page. Then it finds the first Finish button and "clicks" it. (The "onbeforeunload" event won't fire again because the "click" event disables it when it executes.)
Requirement: You can't go back and change your answers once you submit them.
I have found no way to do this in the out-of-the-box surveys in SharePoint. At first I thought I could create a new Permission Level that gave only Add and View permission (no Edit or Delete permission). I did this and assigned that Add-and-View permission level to the appropriate group for the survey. But it always results in an Access Denied error when a user tries to respond to the survey. It would not allow the users to respond to the survey unless they had Edit permission.
That makes no sense to me - the user is adding a response - why do they need Edit permission?
Since that was out as an option, there was only one thing I could think of - insert some Javascript code in the survey's EditForm.aspx to redirect them out of that form if they tried to change their answers after submission.
Using SPD, I opened the survey's EditForm.aspx and after the </style> tag, I added this code:
<script type="text/javascript">
alert("Sorry, you cannot change your answers once you have finished the quiz.");
window.location = "/SomeLocation/In/SharePoint";
</script>
All this does is display a message, then redirect somewhere else. No editing allowed.
Requirement: You only have 10 minutes to take the quiz.
We want to put a time limit on the amount of time a user had to take the quiz. This should discourage them from taking the time to "get a lifeline" by calling or asking someone else, or from firing up a laptop to look up an answer on the web.
Of course, there is nothing built into SharePoint surveys to handle this, but it can be done with a little Javascript code insert into the survey's NewForm.aspx, which is the page the user sees when they respond to a survey.
We can use the Javascript setTimeout() function to fire off an event after a certain amount of time. In our case, we want to fire an event after 10 minutes. But do what? We want to submit the survey results - but the Finish button is not a "submit" button, so we can't use the submit() function of the form. We need to "click" the Finish button. We can do that with some Javascript - we need to first find the Finish button, then click it. So add this code to NewForm.aspx, just after the closing </table> tag after the closing </WebPartPages:WebPartZone> tag:
<script type="text/javascript">
function findFinish()
{
var btn = null;
var e = document.getElementsByTagName("input");
for(var i = 0; i < e.length; i++)
{
if (e[i].type == 'button' && e[i].value == 'Finish')
{
btn = e[i];
break;
}
}
return btn;
}
function endQuiz()
{
alert('Sorry, your time is up! Your answers will now be recorded.');
var btn = findFinish();
if (btn != null)
btn.click();
}
setTimeout("endQuiz()",60000*10);
</script>
This code will execute the endQuiz() function after 10 minutes (60000 milliseconds = 1 minute, times 10). endQuiz() displays a message with alert(), then finds the Finish button by looking for an input tag that is a button with the value (label) "Finish", then clicks it, submitting the survey results.
NOTE: You must be sure that no questions are set up as requiring an answer. If you have any that are required, it will kick the form back open and wait for the user to answer the required questions, defeating the purpose of the timeout.
Requirement: Prevent "cheating" as much as possible.
As explained at the beginning of this post, this is a trivia quiz for fun, but there is a prize involved. So we want to take some measures to prevent "cheating", but we aren't talking about doing the SAT's, bar exam, or medical boards here.... We just want to make it somewhat difficult, and there are a few easy steps we can take.
First, we can prevent the user from printing the questions on the browser page (perhaps to give them to a partner in crime). Insert this just after the closing </table> tag after the closing </WebPartPages:WebPartZone> tag:
<style type="text/css" media="print">
BODY { display:none; visibility:hidden; }
</style>
This uses CSS to set the entire body's content to hidden for the media type "print". So if the print button in the browser is clicked, the page will be blank. (There is no way I can see to prevent the PrintScreen key from being used to capture a screen image, so there is still that option - again, we are just trying to make things difficult, it's not foolproof.)
Second, we can disable the right-click context menu to prevent Select All and Copy, so these can't be used to copy the questions to the clipboard. We can also capture the "SelectStart" event and short circuit it, which should stop Control-A being used to select everything on the page (to then copy with Control-C). These measures work in Internet Explorer, but may not work with other browsers. In our case, we use IE only. So add this Javascript code to NewForm.aspx:
document.body.oncontextmenu = function() {return false;}
document.body.onselectstart = function() {return false;}
The third and final measure we can take is to prevent users from trying to "Google" some answers. In a way, the time limit helps with this. If you have enough questions in the quiz and an aggressive time limit, trying to look up answers will end up taking too much time and you will have a few right answers, but many will be left unanswered.
We can't prevent the user from opening other browser windows or tabs, but we can detect whether our survey page loses focus, meaning the user possibly tried to look something up. It could be they were doing something innocent like reading an e-mail, but as part of our rules, we are telling the users that once they start, they must stay on that page and finish the entire quiz. Clicking off the page will automatically submit what they have answered so far.
The loss of focus on the page can be detected in Internet Explorer with the "onblur" event. It's a strange name, but it's defined in the documentation as "when the object loses the input focus". If we trap this event on the "window" object, we can tell when the current browser window loses focus, meaning the user clicked off the page and tried to do something else. If this happens, we want to submit the current answers by "clicking" the Finish button. This works exactly the same way as how we trapped the "onbeforeunload" event above. Just do the same thing for "onblur".
Summary
If you want all of the measures described above to be taken, do the following:
1. Set "Allow multiple responses" to No.
2. Set Item-level Permissions for Read and Edit to "Only their own".
3. Put the survey on a page as a web part, and set the web part to use the Summary toolbar and Chrome Type of None.
4. Use the Content Editor Web Part on that page to hide "View All Site Content" and/or rename the overview.aspx page for the survey.
5. Edit the survey's EditForm.aspx page and add the Javascript given above to redirect away from it so answers cannot be changed.
6. Add the following code to the NewForm.aspx page, after the closing </table> tag after the closing </WebPartPages:WebPartZone> tag, to time the response, prevent navigation away from the page, and prevent copying or printing:
<style type="text/css" media="print">
BODY { display:none; visibility:hidden; }
</style>
<script type="text/javascript">
function findFinish(which)
{
var btn = null;
var count = 1;
var e = document.getElementsByTagName("input");
for(var i = 0; i < e.length; i++)
{
if (e[i].type == 'button' && e[i].value == 'Finish')
{
if (count == which)
{
btn = e[i];
break;
}
else
{
count++;
}
}
}
return btn;
}
function hideCancel()
{
var e = document.getElementsByTagName("input");
for(var i = 0; i < e.length; i++)
if (e[i].type == 'button' && e[i].value == 'Cancel')
e[i].style.visibility = "hidden";
}
function insertSubmitEvent()
{
var btn = findFinish(1);
if (btn != null)
{
if (typeof btn.onclick == "function")
{
var oldClick = btn.onclick;
btn.onclick = function() { window.onbeforeunload = null; window.onblur = null; oldClick(); }
}
}
var btn2 = findFinish(2);
if (btn2 != null)
{
if (typeof btn2.onclick == "function")
{
var oldClick = btn2.onclick;
btn2.onclick = function() { window.onbeforeunload = null; window.onblur = null; oldClick(); }
}
}
}
function endQuiz()
{
alert('Sorry, your time is up! Your answers will now be recorded.');
window.onbeforeunload = null;
window.onblur = null;
var btn = findFinish(1);
if (btn != null)
btn.click();
}
function forceSubmit()
{
alert('Since you clicked off the page, your answers will now be recorded - you were warned!');
var btn = findFinish(1);
if (btn != null)
btn.click();
}
setTimeout("endQuiz()",60000*10);
insertSubmitEvent();
hideCancel();
window.onbeforeunload = forceSubmit;
window.onblur = forceSubmit;
document.body.oncontextmenu = function() {return false;}
document.body.onselectstart = function() {return false;}
</script>
In part 2, we will look at how to easily "grade" the quiz and find out who got the best score.