SPServices Stories #6 – Custom Quizzing System

This entry is part 6 of 21 in the series SPServices Stories

Introduction

Many times, SPServices Stories play out on the field of the Codeplex Discussions. As someone posts questions about how to build some of the components of their solution, the overall plan comes into focus.

Over the last week or so, I’ve been helping Oli Howson (@Mr_Howson) in the discussions here and here. As the bits and pieces came to together for me, I thought that Oli’s work in progress would make a great SPServices Story, especially since he took the trouble to write up what he was trying to accomplish in the discussion thread.

Oli’s project is in process, and it’s certainly possible that he will make changes from here. However, I thought it would be useful to post things “as-is” so that everyone could see how he’s going about it. If he makes any significant changes, we’ll try to post them back as well.

If you have any comments for Oli about how you might do things differently, I’m sure he’d be interested. I know I would be.

Oli’s entire bespoke page is shown below, so you get to see the markup, the script, everything.

Custom Quizzing System

I am a teacher – running the ICT and Computer Science department of a South London Academy – we teach both disciplines to 11-18 year olds. For key-stage 3 (Y7, 8 and 9) we have for the last few years set homework on our VLE (Virtual Learning Environment) which had a built-in testing system. Due to that being about the only part of the VLE that wasn’t naff, it was recently retired and a new SharePoint system brought in. It’s got a few bespoke bits, and I am not an admin. Now I’m faced with the dilemma: I have 555-ish students needing their homework setting every week, I deliver all my learning resources via the SharePoint system, but there is no built-in quizzing system. Yes – there are surveys, but they don’t mark and have their own foibles. So I built this JavaScript-based quizzing system.

Methodology

The teacher in charge of setting homework that week creates a multiple-choice quiz on a stand-alone piece of client software I wrote in Delphi. This then creates an array string which it pastes into the quiz template (with a relevant name) and copies the file to the relevant document store in the SharePoint server. The teacher then just creates a link from the homework page to the relevant quiz, and when the kids hit the quiz the results go into the relevant list (created with the same name as the quiz). The difficult bit was making sure that the list was created the first time the quiz is run. The idea is the teacher hits the quiz once the link is up to make sure it has worked. When they submit, it creates the list, adds the columns, and updates the view. The second time it tests if the list exists (it does now!) and just inserts their score, which it also shows to the kids and then closes the window.

Well, I think I’m there! I’m going to get this beta tested by a group of Year 9 students tomorrow, but I’ll put the code below for reference to anyone that might find it interesting. I’m sure I’ve made loads of faux-pas as I’ve written a grand total of about three things in JavaScript, and have very limited knowledge of SharePoint.

Wheeee :)

[important]Code update with Oli’s changes in version 1.1.0 on 2013-02-14[/important]

1<!doctype html>
2 
3<html lang="en">
4<head>
5 <meta charset="utf-8" />
6 <title>jQuery UI Tabs - Default functionality</title>
7 <link rel="stylesheet" href="/mertonshared/computerstudies/homework/Documents/includes/jquery-ui.css" />
8 <script language="javascript" src="/mertonshared/computerstudies/homework/Documents/includes/jquery-1.8.1.js"></script>
9 <script language="javascript" src="/mertonshared/computerstudies/homework/Documents/includes/jquery-ui.js"></script>
10 <script language="javascript" src="/mertonshared/computerstudies/homework/Documents/includes/jquery.SPServices-0.7.2.js" type="text/javascript"></script>
11 <script>
12 ///////////////////////////////////////////////////////////////////////////////////////////////////
13 // Quizzer v1.0.2                                                                                //
14 // Change Log:                                                                                   //
15 //  - 1.1.0 Added check whether user has completed before - one try only. Reordered some actions //
16 //          Added permissions setting when list is being created. Changed submit button value.   //
17 //  - 1.0.2 Removed some pointless alerts for ordinary users, reenabled windows.close(), added   //
18 //           some useful comments.                                                               //
19 //  - 1.0.1 Added view change to show all fields                                                 //
20 // Currently available:                                                                          //
21 //  - Multiple choice                                                                            //
22 //  - Unlimited questions                                                                        //
23 //  - Four options per question                                                                  //
24 // Future Plans                                                                                  //
25 //  - Varying number of options per question                                                     //
26 //  - Missing word completion                                                                    //
27 //  - Include YouTube clip                                                                       //
28 ///////////////////////////////////////////////////////////////////////////////////////////////////
29 
30 var startseconds;
31 var filename;
32 var score;
33 var thisUserName;
34 var previouslytried;
35 
36 function checkiflistexists() {
37    //alert('checking existance of list: '+filename);
38    $().SPServices ({
39        operation: "GetList",
40        listName:  filename,
41        completefunc: function (xData, Status) {
42                //tp1 = xData.responseText;
43                //alert(tp1);
44                //alert(Status);
45                if (Status == 'success') {
46                    //alert ('Exists - dont create - just insert data');
47                    insertdata();
48                }
49                else {
50                    alert('Dont exist');
51                    $().SPServices({
52                        operation: "AddList",
53                        listName: filename,
54                        description: "List created for quiz: "+filename,
55                        templateID: 100,
56                        completefunc: function (xData, Status) {
57                            alert('Trying to create list');
58                            alert(Status);
59                            alert('Now add fields');
60                            var nfields = "<Fields><Method ID='1'><Field Type='Text' DisplayName='Score' ResultType='Text'></Field></Method><Method ID='2'><Field Type='Text' DisplayName='TimeTaken' ResultsType='Text'></Field></Method><Method ID='3'><Field Type='Text' DisplayName='CompletedOn' ResultsType='Text'></Field></Method></Fields>";
61                            $().SPServices({
62                                operation: 'UpdateList',
63                                listName: filename,
64                                newFields: nfields,
65                                completefunc: function(xData, Status) {
66                                    tp1 = xData.responseText;
67                                    tp4 = tp1.indexOf("errorstring");
68                                    if (tp4 < 0) {
69                                        alert("Fields created! - Update View");
70                                        var viewname = "";
71                                        $().SPServices({
72                                            operation: "GetViewCollection",
73                                            async: false,
74                                            listName: filename,
75                                            completefunc: function (xData, Status) {
76                                                alert('Complete Func - GewViewCollection');
77                                                $(xData.responseXML).find("[nodeName='View']").each(function() {
78                                                    var viewdisplayname = $(this).attr("DisplayName");
79                                                    if (viewdisplayname=="AllItems") {
80                                                        viewname = $(this).attr("Name");
81                                                        return false;
82                                                    }
83                                                });
84                                            }
85                                        });
86                                        alert('Ok - done GetViewCollection - now update the view');
87                                        var viewfields = "<ViewFields><FieldRef Name=\"Title\" /><FieldRef Name=\"Score\" /><FieldRef Name=\"TimeTaken\" /><FieldRef Name=\"CompletedOn\" /></ViewFields>";
88                                        $().SPServices({
89                                            operation: 'UpdateView',
90                                            async: false,
91                                            listName: filename,
92                                            viewName: viewname,
93                                            viewFields: viewfields,
94                                            completefunc: function(xData, Status) {
95                                                alert('Trying to update view');
96                                                alert(Status);
97                                                alert('Updated view - now update permissions');
98                                                //insertdata();
99                                                $().SPServices({
100                                                    operation: 'AddPermission',
101                                                    objectType: 'List',
102                                                    objectName: filename,
103                                                    permissionIdentifier: "HARRISNET\\ham-grp-students",
104                                                    permissionType: 'user',
105                                                    permissionMask: 1011028719,
106                                                    completefunc: function(xData, Status) {
107                                                        alert('Trying to update permissions');
108                                                        alert(Status);
109                                                        alert(xData.responseXML.xml);
110                                                        alert('Done... hopefully! - better insert the data!');
111                                                        insertdata();
112                                                    }
113                                                });
114                                            }
115                                        });
116                                    }
117                                    else {
118                                    // Error creating fields!
119                                    alert("Error creating fields!");
120                                    }
121                                }
122                            });
123                        }
124                    });
125                }
126            }
127        });
128 }
129 
130 function insertdata() {
131    var endseconds = new Date().getTime() / 1000;
132    endseconds = endseconds - startseconds;
133    var d = new Date();
134    var dd = d.toDateString();
135    var dt = d.toTimeString();
136    $().SPServices({
137        operation: "UpdateListItems",
138        async: false,
139        batchCmd: "New",
140        listName: filename,
141        valuepairs: [["Title", thisUserName],["Score",score],["TimeTaken",Math.round(endseconds).toString()+" seconds"],["CompletedOn",dd+" "+dt]],
142        completefunc: function (xData, Status) {
143            //alert('Trying to add data');
144            if (Status == 'success') {
145                //inserted();
146            }
147            else {
148                alert(Status+' : There was a problem inserting your score into the database. Please notify Mr Howson!');
149                //inserted();
150            }
151        }
152    });
153    alert('You achieved a score of '+score);
154    window.close();
155 }
156 
157 function checkanswers() {
158    var form = document.getElementById('answers');
159    score = 0;
160    for (var i = 0; i < form.elements.length; i++ ) {
161        if (form.elements[i].type == 'radio') {
162            if (form.elements[i].checked == true) {
163                if (questions[form.elements[i].name.substring(9)-1][questions[form.elements[i].name.substring(9)-1].length-1] == form.elements[i].value)
164                {
165                 score++;
166                }
167            }
168        }
169    }
170  $(document).ready(function() {
171    checkiflistexists();
172  });
173 }
174 
175 function initialise() {
176    var rowcount = 0;
177    startseconds = new Date().getTime() / 1000;
178    var url = window.location.pathname;
179    thisUserName = $().SPServices.SPGetCurrentUser({
180        fieldName: "Title",
181        debug: false
182    });
183    filename = url.substring(url.lastIndexOf('/')+1);
184    filename = filename.substring(0,filename.lastIndexOf('.'));
185    //alert(filename);
186    //alert('Getting items');
187  $().SPServices({
188    operation: "GetListItems",
189    async: false,
190    listName: filename,
191    CAMLViewFields: "<ViewFields><FieldRef Name='Title' /><FieldRef Name='Score' /></ViewFields>",
192    CAMLQuery: "<Query><Where><Eq><FieldRef Name='Title' /><Value Type='Text'>"+thisUserName+"</Value></Eq></Where></Query>",
193    CAMLRowLimit: 1,
194    completefunc: function (xData, Status) {
195      $(xData.responseXML).SPFilterNode("z:row").each(function() {
196        //var liHtml = "<li>" + $(this).attr("ows_Title") + "</li>";
197        score = $(this).attr("ows_Score");
198        //$("#tasksUL").append(liHtml);
199        rowcount++;
200      });
201    }
202  });
203    //alert(rowcount);
204    //alert('done');
205  if (rowcount > 0) {
206   previouslytried = true;
207   document.getElementById('tabs').style.visibility = 'hidden';
208   alert('Sorry - you have already tried this quiz - one try only!\nLast time you got a '+score);
209   window.close();
210  }
211  else {
212   previouslytried = false;
213  }
214 }
215 
216 function inserted() {
217    //window.close();
218 }
219 
220 $(function() {
221  $( "#tabs" ).tabs();
222 });
223 
224 var questions = new Array;
225  //The section below should be uncommented when not testing - this will be replaced by the client
226  // side application with the questions array.
227  [INSERTQUESTIONS]
228 
229 //The following questions can be uncommented for testing purposes
230 //questions[0] = ['q1','a','b','c',1];
231 //questions[1] = ['q2','d','e','f',2];
232 //questions[2] = ['q3','g','h','i',3];
233 </script>
234</head>
235 
236<body onload="initialise()">
237 <div id="tabs">
238  <ul>
239   <script language="JavaScript">
240    for (var i = 0; i< questions.length; i++)
241    {
242     document.write('<li><a href="#tabs-'+(i+1)+'">Question '+(i+1)+'</a></li>');
243    }
244    document.write('<li><a href="#tabs-'+(i+1)+'">Summary</a></li>');
245   </script>
246  </ul>
247   <form name="answers" id="answers">
248   <script language="JavaScript">
249    for (var i = 0; i < questions.length; i++)
250    {
251     document.write('<div id="tabs-'+(i+1)+'">');
252     document.write(' <p>'+questions[i][0]+'</p>');
253     for (var j = 1; j < questions[i].length-1; j++)
254     {
255      document.write(' <p><input type="radio" name="question-'+(i+1)+'" value="'+j+'">'+questions[i][j]+'<br></p>');
256     }
257     document.write('</div>');
258    }
259    document.write('<div id="tabs-'+(i+1)+'">');
260    document.write(' <p><input type="submit" onclick="checkanswers(); return false;" value="Submit Homework"></p>');
261    document.write('</div>');
262  </script>
263  </form>
264 </div>
265</body>
266</html>
Series Navigation<< SPServices Stories #5 – Gritter and Sharepoint: Integrate Nifty Notifications in Your Intranet!SPServices Stories #7 – Example Uses of SPServices, JavaScript and SharePoint >>

Similar Posts

2 Comments

  1. Updated on the codeplex site – added permissions setter when creating the list, one try only, and a few tweaks.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.