3 define('AT_INCLUDE_PATH', '../../../include/');
4 require(AT_INCLUDE_PATH.'config.inc.php');
5 require_once(AT_INCLUDE_PATH.'lib/mysql_connect.inc.php');
7 require_once("ims-blti/OAuth.php");
8 require_once("TrivialStore.php");
10 error_reporting(E_ALL & ~E_NOTICE);
11 ini_set("display_errors", 1);
13 function message_response($major, $severity, $minor=false, $message=false, $xml=false) {
14 $lti_message_type = $_REQUEST['lti_message_type'];
15 $retval = '<?xml version="1.0" encoding="UTF-8"?>'."\n" .
16 "<message_response>\n" .
17 " <lti_message_type>$lti_message_type</lti_message_type>\n" .
19 " <codemajor>$major</codemajor>\n" .
20 " <severity>$severity</severity>\n";
21 if ( ! $codeminor === false ) $retval = $retval . " <codeminor>$minor</codeminor>\n";
23 " <description>$message</description>\n" .
25 if ( ! $xml === false ) $retval = $retval . $xml;
26 $retval = $retval . "</message_response>\n";
30 function doError($message) {
31 print message_response('Fail', 'Error', false, $message);
35 $lti_version = $_REQUEST['lti_version'];
36 if ( $lti_version != "LTI-1p0" ) doError("Improperly formed message");
38 $lti_message_type = $_REQUEST['lti_message_type'];
39 if ( ! isset($lti_message_type) ) doError("Improperly formed message");
41 $message_type = false;
42 if( $lti_message_type == "basic-lis-replaceresult" ||
43 $lti_message_type == "basic-lis-createresult" ||
44 $lti_message_type == "basic-lis-updateresult" ||
45 $lti_message_type == "basic-lis-deleteresult" ||
46 $lti_message_type == "basic-lis-readresult" ) {
47 $sourcedid = $_REQUEST['sourcedid'];
48 $message_type = "basicoutcome";
49 } else if ( $lti_message_type == "basic-lti-loadsetting" ||
50 $lti_message_type == "basic-lti-savesetting" ||
51 $lti_message_type == "basic-lti-deletesetting" ) {
52 $sourcedid = $_REQUEST['id'];
53 $message_type = "toolsetting";
54 } else if ( $lti_message_type == "basic-lis-readmembershipsforcontext") {
55 $sourcedid = $_REQUEST['id'];
56 $message_type = "roster";
59 if ( $message_type == false ) {
60 doError("Illegal lti_message_type");
63 if ( !isset($sourcedid) ) {
64 doError("sourcedid missing");
66 // Truncate to maximum length
67 $sourcedid = substr($sourcedid, 0, 2048);
70 $info = explode(':::',$sourcedid);
71 if ( ! is_array($info) ) doError("Bad sourcedid");
72 $signature = $info[0];
73 $userid = intval($info[1]);
74 $placement = $info[2];
77 doError("Bad sourcedid");
80 if ( isset($signature) && isset($userid) && isset($placement) ) {
83 doError("Bad sourcedid");
86 function loadError($msg) {
90 $content_id = $placement;
92 require("loadrows.php");
93 $course_id = $atutor_content_row['course_id'];
94 // echo("basiclti_content_row<br/>\n");print_r($basiclti_content_row); echo("<hr>\n");
95 // echo("basiclti_tool_row<br/>\n");print_r($basiclti_tool_row); echo("<hr>\n");
96 // echo("atutor_content_row<br/>\n");print_r($atutor_content_row); echo("<hr>\n");
97 // echo("atutor_course_row<br/>\n");print_r($atutor_course_row); echo("<hr>\n");
98 // These two might not be important here
99 // echo("atutor_member_row<br/>\n");print_r($atutor_member_row); echo("<hr>\n");
100 // echo("atutor_course_membership_row<br/>\n");print_r($atutor_course_membership_row); echo("<hr>\n");
102 if ( $message_type == "basicoutcome" ) {
103 if ( $basiclti_tool_row['acceptgrades'] == 1 && $basiclti_content_row['gradebook_test_id'] > 0 ) {
104 // The placement is configured to accept grades
106 doError("Not permitted");
108 } else if ( $message_type == "roster" ) {
109 if ( $basiclti_tool_row['allowroster'] == 1 ||
110 ( $basiclti_tool_row['allowroster'] == 2 && $basiclti_content_row['allowroster'] == 1 ) ) {
113 doError("Not permitted");
115 } else if ( $message_type == "toolsetting" ) {
116 if ( $basiclti_tool_row['allowsetting'] == 1 ||
117 ( $basiclti_tool_row['allowsetting'] == 2 && $basiclti_content_row['allowsetting'] == 1 ) ) {
120 doError("Not permitted");
124 // Retrieve the secret we use to sign lis_result_sourcedid
125 $placementsecret = $basiclti_content_row['placementsecret'];
126 $oldplacementsecret = $basiclti_content_row['oldplacementsecret'];
127 if ( ! isset($placementsecret) ) doError("Not permitted");
129 $suffix = ':::' . $userid . ':::' . $placement;
130 $plaintext = $placementsecret . $suffix;
131 $hashsig = hash('sha256', $plaintext, false);
132 if ( $hashsig != $signature && isset($oldplacementsecret) && strlen($oldplacementsecret) > 1 ) {
133 $plaintext = $oldplacementsecret . $suffix;
134 $hashsig = hash('sha256', $plaintext, false);
137 if ( $hashsig != $signature ) {
138 doError("Invalid sourcedid");
141 // Check the OAuth Signature
142 $oauth_consumer_key = $basiclti_tool_row['resourcekey'];
143 $oauth_secret = $basiclti_tool_row['password'];
145 if ( ! isset($oauth_secret) ) doError("Not permitted");
146 if ( ! isset($oauth_consumer_key) ) doError("Not permitted");
148 // Verify the message signature
149 $store = new TrivialOAuthDataStore();
150 $store->add_consumer($oauth_consumer_key, $oauth_secret);
152 $server = new OAuthServer($store);
154 $method = new OAuthSignatureMethod_HMAC_SHA1();
155 $server->add_signature_method($method);
156 $request = OAuthRequest::from_request();
158 $basestring = $request->get_signature_base_string();
161 $server->verify_request($request);
162 } catch (Exception $e) {
163 doError($e->getMessage());
166 // Beginning of actual grade processing
167 if ( $message_type == "basicoutcome" ) {
168 if ( ! isset( $basiclti_content_row['gradebook_test_id'] ) ) {
169 doError("Not permitted");
172 // TODO: Greg - Is this appropriate? It would be nice to allow this.
173 if ( $atutor_course_membership_row['role'] == 'Instructor' ) {
174 doError('Grades not supported for instructors');
177 $gradebook_test_id = $basiclti_content_row['gradebook_test_id'];
179 // Check to see if this grade is in this course and member is in this course
180 // And that this grade item is of the right type
181 $sql = 'SELECT role,m.member_id AS member_id,first_name,last_name,email
182 FROM '.TABLE_PREFIX.'gradebook_tests AS g
183 JOIN '.TABLE_PREFIX.'course_enrollment AS e
184 JOIN '.TABLE_PREFIX.'members AS m
185 ON g.course_id = e.course_id AND e.member_id = m.member_id
186 WHERE e.course_id = '.$course_id.' AND m.member_id ='.$member_id.'
187 AND g.gradebook_test_id = '.$gradebook_test_id."
188 AND g.type = 'External' and g.grade_scale_id = 0";
189 $gradebook_result = mysql_query($sql, $db);
190 $count = mysql_num_rows($gradebook_result);
192 doError("Not gradable");
195 $read_sql = 'SELECT d.grade AS grade
196 FROM '.TABLE_PREFIX.'gradebook_detail AS d
197 JOIN '.TABLE_PREFIX.'gradebook_tests AS g
198 JOIN '.TABLE_PREFIX.'course_enrollment AS e
199 JOIN '.TABLE_PREFIX.'members AS m
200 ON d.gradebook_test_id = g.gradebook_test_id
201 AND g.course_id = e.course_id AND e.member_id = m.member_id
202 WHERE e.course_id = '.$course_id.' AND d.member_id ='.$member_id.'
203 AND g.gradebook_test_id = '.$gradebook_test_id."
204 AND g.type = 'External' and g.grade_scale_id = 0";
206 if ( $lti_message_type == "basic-lis-readresult" ) {
207 $grade_result = mysql_query($read_sql, $db);
208 $count = mysql_num_rows($gradebook_result);
210 doError("Not gradable");
213 $grade_row = mysql_fetch_assoc($grade_result);
214 if ( $grade_row === false ) {
216 } else if ( isset($grade_row['grade']) ) {
217 $grade = $grade_row['grade'];
220 if ( ! isset($grade) ) {
221 doError("Unable to read grade");
224 $result = " <result>\n" .
227 htmlspecialchars($grade*1.0) .
229 " </resultscore>\n" .
231 print message_response('Success', 'Status', false, "Grade read", $result);
235 if ( $lti_message_type == "basic-lis-deleteresult" ) {
236 $delete_sql = 'DELETE FROM '.TABLE_PREFIX.'gradebook_detail
237 WHERE member_id ='.$member_id.'
238 AND gradebook_test_id = '.$gradebook_test_id;
240 $gradebook_result = mysql_query($delete_sql, $db);
241 if ( $gradebook_result === false ) {
242 doError("Could not delete grade");
244 print message_response('Success', 'Status', 'fullsuccess', 'Grade deleted');
248 if ( isset($_REQUEST['result_resultscore_textstring']) && strlen($_REQUEST['result_resultscore_textstring']) > 0) {
249 $gradeval = floatval($_REQUEST['result_resultscore_textstring']);
251 if ( $gradeval < 0.0 || $gradeval > 1.0 ) {
252 doError('Invalid Grade');
255 // TODO: Greg - do we do Insert or Update?
256 $replace_sql = 'INSERT INTO '.TABLE_PREFIX.'gradebook_detail
257 (gradebook_test_id, member_id, grade) VALUES
258 ('.$gradebook_test_id.','.$member_id.','.$gradeval.')
259 ON DUPLICATE KEY UPDATE grade='.$gradeval;
261 $gradebook_result = mysql_query($replace_sql, $db);
262 if ( $gradebook_result === false ) {
263 // TODO: Log message would be good here
264 doError("Could not store grade");
266 print message_response('Success', 'Status', 'fullsuccess', 'Grade updated');
270 } else if ( $lti_message_type == "basic-lti-loadsetting" ) {
271 $xml = " <setting>\n" .
272 " <value>".htmlspecialchars($basiclti_content_row['setting'])."</value>\n" .
274 print message_response('Success', 'Status', 'fullsuccess', 'Setting retrieved', $xml);
275 } else if ( $lti_message_type == "basic-lti-savesetting" ) {
276 $setting = $_REQUEST['setting'];
277 if ( ! isset($setting) ) doError('Missing setting value');
278 // $sql = "UPDATE {$CFG->prefix}basiclti SET
279 // setting='". mysql_escape_string($setting) . "' WHERE id=" . $basiclti->id;
280 $sql = "UPDATE ".TABLE_PREFIX."basiclti_content
281 SET setting='". mysql_escape_string($setting) . "' WHERE content_id=" . $placement;
282 $success = mysql_query($sql);
284 print message_response('Success', 'Status', 'fullsuccess', 'Setting updated');
286 doError("Error updating setting");
288 } else if ( $lti_message_type == "basic-lti-deletesetting" ) {
289 $sql = "UPDATE ".TABLE_PREFIX."basiclti_content
290 SET setting='' WHERE content_id=" . $placement;
291 $success = mysql_query($sql);
293 print message_response('Success', 'Status', 'fullsuccess', 'Setting deleted');
295 doError("Error updating setting");
297 } else if ( $message_type == "roster" ) {
298 $sql = 'SELECT role,m.member_id AS member_id,first_name,last_name,email
299 FROM '.TABLE_PREFIX.'course_enrollment AS e
300 JOIN '.TABLE_PREFIX.'members AS m ON e.member_id = m.member_id
301 WHERE course_id = '.$course_id;
302 $roster_result = mysql_query($sql, $db);
303 $xml = " <memberships>\n";
304 while ($row = mysql_fetch_assoc($roster_result)) {
306 if ( $row['role'] == 'Instructor' ) $role = 'Instructor';
307 $userxml = " <member>\n".
308 " <user_id>".htmlspecialchars($row['member_id'])."</user_id>\n".
309 " <roles>$role</roles>\n";
310 if ( $basiclti_tool_row['sendname'] == 1 ||
311 ( $basiclti_tool_row['sendname'] == 2 && $basiclti_content_row['sendname'] == 1 ) ) {
312 if ( isset($row['first_name']) ) $userxml .= " <person_name_given>".htmlspecialchars($row['first_name'])."</person_name_given>\n";
313 if ( isset($row['last_name']) ) $userxml .= " <person_name_family>".htmlspecialchars($row['last_name'])."</person_name_family>\n";
315 if ( $basiclti_tool_row['sendemailaddr'] == 1 ||
316 ( $basiclti_tool_row['sendemailaddr'] == 2 && $basiclti_content_row['sendemailaddr'] == 1 ) ) {
317 if ( isset($row['email']) ) $userxml .= " <person_contact_email_primary>".htmlspecialchars($row['email'])."</person_contact_email_primary>\n";
319 if ( isset($placementsecret) ) {
320 $suffix = ':::' . $row['member_id'] . ':::' . $placement;
321 $plaintext = $placementsecret . $suffix;
322 $hashsig = hash('sha256', $plaintext, false);
323 $sourcedid = $hashsig . $suffix;
325 if ( $basiclti_tool_row['acceptgrades'] == 1 && $basiclti_content_row['gradebook_test_id'] > 0 ) {
326 if ( isset($sourcedid) ) $userxml .= " <lis_result_sourcedid>".htmlspecialchars($sourcedid)."</lis_result_sourcedid>\n";
328 $userxml .= " </member>\n";
331 $xml .= " </memberships>\n";
332 print message_response('Success', 'Status', 'fullsuccess', 'Roster retreived', $xml);