Skip to content
Browse files

Adding oauth body code (from Chuck), implemented outcome services (on…

…ly replaceResult tested)
  • Loading branch information...
1 parent aa6eca6 commit b9b2e7bbf821a9b451f5ac84b7b7f00c8365ae94 @scriby scriby committed
Showing with 1,704 additions and 1,301 deletions.
  1. +158 −0 mod/lti/OAuthBody.php
  2. +925 −910 mod/lti/locallib.php
  3. +391 −0 mod/lti/oldservice.php
  4. +230 −391 mod/lti/service.php
View
158 mod/lti/OAuthBody.php
@@ -0,0 +1,158 @@
+<?php
+// This file is part of BasicLTI4Moodle
+//
+// BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)
+// consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web
+// based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI
+// specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS
+// are already supporting or going to support BasicLTI. This project Implements the consumer
+// for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.
+// BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem
+// at the GESSI research group at UPC.
+// SimpleLTI consumer for Moodle is an implementation of the early specification of LTI
+// by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a
+// Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.
+//
+// BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis
+// of the Universitat Politecnica de Catalunya http://www.upc.edu
+// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu
+//
+// OAuthBody.php is distributed under the MIT License
+//
+// The MIT License
+//
+// Copyright (c) 2007 Andy Smith
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+require_once($CFG->dirroot . '/mod/lti/OAuth.php');
+require_once($CFG->dirroot . '/mod/lti/TrivialStore.php');
+
+function getOAuthKeyFromHeaders()
+{
+ $request_headers = OAuthUtil::get_headers();
+ // print_r($request_headers);
+
+ if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
+ $header_parameters = OAuthUtil::split_header($request_headers['Authorization']);
+
+ // echo("HEADER PARMS=\n");
+ // print_r($header_parameters);
+ return $header_parameters['oauth_consumer_key'];
+ }
+ return false;
+}
+
+function handleOAuthBodyPOST($oauth_consumer_key, $oauth_consumer_secret)
+{
+ $request_headers = OAuthUtil::get_headers();
+ // print_r($request_headers);
+
+ // Must reject application/x-www-form-urlencoded
+ if ($request_headers['Content-type'] == 'application/x-www-form-urlencoded' ) {
+ throw new Exception("OAuth request body signing must not use application/x-www-form-urlencoded");
+ }
+
+ if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
+ $header_parameters = OAuthUtil::split_header($request_headers['Authorization']);
+
+ // echo("HEADER PARMS=\n");
+ // print_r($header_parameters);
+ $oauth_body_hash = $header_parameters['oauth_body_hash'];
+ // echo("OBH=".$oauth_body_hash."\n");
+ }
+
+ if ( ! isset($oauth_body_hash) ) {
+ throw new Exception("OAuth request body signing requires oauth_body_hash body");
+ }
+
+ // Verify the message signature
+ $store = new TrivialOAuthDataStore();
+ $store->add_consumer($oauth_consumer_key, $oauth_consumer_secret);
+
+ $server = new OAuthServer($store);
+
+ $method = new OAuthSignatureMethod_HMAC_SHA1();
+ $server->add_signature_method($method);
+ $request = OAuthRequest::from_request();
+
+ try {
+ $server->verify_request($request);
+ } catch (Exception $e) {
+ $message = $e->getMessage();
+ throw new Exception("OAuth signature failed: " . $message);
+ }
+
+ $postdata = file_get_contents('php://input');
+ // echo($postdata);
+
+ $hash = base64_encode(sha1($postdata, TRUE));
+
+ if ( $hash != $oauth_body_hash ) {
+ throw new Exception("OAuth oauth_body_hash mismatch");
+ }
+
+ return $postdata;
+}
+
+function sendOAuthBodyPOST($method, $endpoint, $oauth_consumer_key, $oauth_consumer_secret, $content_type, $body)
+{
+ $hash = base64_encode(sha1($body, TRUE));
+
+ $parms = array('oauth_body_hash' => $hash);
+
+ $test_token = '';
+ $hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+ $test_consumer = new OAuthConsumer($oauth_consumer_key, $oauth_consumer_secret, NULL);
+
+ $acc_req = OAuthRequest::from_consumer_and_token($test_consumer, $test_token, $method, $endpoint, $parms);
+ $acc_req->sign_request($hmac_method, $test_consumer, $test_token);
+
+ $header = $acc_req->to_header();
+ $header = $header . "\r\nContent-type: " . $content_type . "\r\n";
+
+ $params = array('http' => array(
+ 'method' => 'POST',
+ 'content' => $body,
+ 'header' => $header
+ ));
+ $ctx = stream_context_create($params);
+ $fp = @fopen($endpoint, 'rb', false, $ctx);
+ if (!$fp) {
+ throw new Exception("Problem with $endpoint, $php_errormsg");
+ }
+ $response = @stream_get_contents($fp);
+ if ($response === false) {
+ throw new Exception("Problem reading data from $endpoint, $php_errormsg");
+ }
+ return $response;
+}
View
1,835 mod/lti/locallib.php
925 additions, 910 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
391 mod/lti/oldservice.php
@@ -0,0 +1,391 @@
+<?php
+// This file is part of BasicLTI4Moodle
+//
+// BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)
+// consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web
+// based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI
+// specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS
+// are already supporting or going to support BasicLTI. This project Implements the consumer
+// for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.
+// BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem
+// at the GESSI research group at UPC.
+// SimpleLTI consumer for Moodle is an implementation of the early specification of LTI
+// by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a
+// Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.
+//
+// BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis
+// of the Universitat Politecnica de Catalunya http://www.upc.edu
+// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains all necessary code to support basiclti services
+ * like outcomes and roster access.
+ *
+ * @package lti
+ * @copyright 2009 Marc Alier, Jordi Piguillem, Nikolas Galanis
+ * marc.alier@upc.edu
+ * @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu
+ *
+ * @author Marc Alier
+ * @author Jordi Piguillem
+ * @author Nikolas Galanis
+ * @author Charles Severance
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once("../../config.php");
+require_once($CFG->dirroot.'/mod/lti/lib.php');
+require_once($CFG->dirroot.'/mod/lti/locallib.php');
+require_once($CFG->dirroot.'/mod/lti/OAuth.php');
+require_once($CFG->dirroot.'/mod/lti/TrivialStore.php');
+
+error_reporting(E_ALL & ~E_NOTICE);
+ini_set("display_errors", 1);
+
+$PAGE->set_context(get_context_instance(CONTEXT_SYSTEM));
+$PAGE->set_url('/mod/lti/service.php');
+$PAGE->set_pagetype('admin-setting-' . $section);
+$PAGE->set_pagelayout('admin');
+$PAGE->navigation->clear_cache();
+
+function message_response($major, $severity, $minor=false, $message=false, $xml=false) {
+ $lti_message_type = $_REQUEST['lti_message_type'];
+ $retval = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"."\n" .
+ "<message_response>\n" .
+ " <lti_message_type>$lti_message_type</lti_message_type>\n" .
+ " <statusinfo>\n" .
+ " <codemajor>$major</codemajor>\n" .
+ " <severity>$severity</severity>\n";
+ if (! $codeminor === false) {
+ $retval = $retval . " <codeminor>$minor</codeminor>\n";
+ }
+ $retval = $retval .
+ " <description>$message</description>\n" .
+ " </statusinfo>\n";
+ if (! $xml === false) {
+ $retval = $retval . $xml;
+ }
+ $retval = $retval . "</message_response>\n";
+ return $retval;
+}
+
+function do_error($message) {
+ print message_response('Fail', 'Error', false, $message);
+ exit();
+}
+
+$lti_version = $_REQUEST['lti_version'];
+if ($lti_version != "LTI-1p0") {
+ do_error("Improperly formed message: wrong lti version: ".$lti_version);
+}
+
+$lti_message_type = $_REQUEST['lti_message_type'];
+if (! isset($lti_message_type)) {
+ do_error("Improperly formed message: no lti_message_type parameter");
+}
+
+$message_type = false;
+if ($lti_message_type == "basic-lis-replaceresult" ||
+ $lti_message_type == "basic-lis-createresult" ||
+ $lti_message_type == "basic-lis-updateresult" ||
+ $lti_message_type == "basic-lis-deleteresult" ||
+ $lti_message_type == "basic-lis-readresult") {
+ $sourcedid = $_REQUEST['sourcedid'];
+ $message_type = "basicoutcome";
+} else if ($lti_message_type == "basic-lti-loadsetting" ||
+ $lti_message_type == "basic-lti-savesetting" ||
+ $lti_message_type == "basic-lti-deletesetting") {
+ $sourcedid = $_REQUEST['id'];
+ $message_type = "toolsetting";
+} else if ($lti_message_type == "basic-lis-readmembershipsforcontext") {
+ $sourcedid = $_REQUEST['id'];
+ $message_type = "roster";
+}
+
+if ($message_type == false) {
+ do_error("Illegal lti_message_type");
+}
+
+if (!isset($sourcedid)) {
+ do_error("sourcedid missing");
+}
+// Truncate to maximum length
+$sourcedid = substr($sourcedid, 0, 2048);
+
+try {
+ $info = explode(':::', $sourcedid);
+ if (! is_array($info)) {
+ do_error("Bad sourcedid (1)");
+ }
+ $signature = $info[0];
+ $userid = intval($info[1]);
+ $placement = $info[2];
+} catch (Exception $e) {
+ do_error("Bad sourcedid (2)");
+}
+
+if (isset($signature) && isset($userid) && isset($placement)) {
+ // OK
+} else {
+ do_error("Bad sourcedid (3)");
+}
+
+// Retrieve the Basic LTI placement
+if (! $basiclti = $DB->get_record('lti', array('id'=>$placement))) {
+ do_error("Bad sourcedid (4)");
+}
+
+$basiclti_types_config = (object)$basiclti_types_config;
+
+$typeconfig = lti_get_type_config($basiclti->typeid);
+
+if (isset($typeconfig) && isset($typeconfig['password'])) {
+ // OK
+} else {
+ do_error("Unable to load type");
+}
+
+if ($message_type == "basicoutcome") {
+ if ($typeconfig["acceptgrades"] == 1 ||
+ ($typeconfig["acceptgrades"] == 2 && $basiclti->instructorchoiceacceptgrades == 1)) {
+ // The placement is configured to accept grades
+ } else {
+ do_error("Not permitted (1)");
+ }
+} else if ($message_type == "toolsetting") {
+ if ($typeconfig["allowsetting"] == 1 ||
+ ($typeconfig["allowsetting"] == 2 && $basiclti->instructorchoiceallowsetting == 1)) {
+ // OK
+ } else {
+ do_error("Not permitted (2)");
+ }
+} else if ($message_type == "roster") {
+ if ($typeconfig["allowroster"] == 1 ||
+ ($typeconfig["allowroster"] == 2 && $basiclti->instructorchoiceallowroster == 1)) {
+ // OK
+ } else {
+ do_error("Not permitted (3)");
+ }
+}
+
+// Retrieve the secret we use to sign lis_result_sourcedid
+$placementsecret = $basiclti->placementsecret;
+$oldplacementsecret = $basiclti->oldplacementsecret;
+if (! isset($placementsecret)) {
+ do_error("Not permitted (4)");
+}
+
+$suffix = ':::' . $userid . ':::' . $placement;
+$plaintext = $placementsecret . $suffix;
+$hashsig = hash('sha256', $plaintext, false);
+if (($hashsig != $signature) && isset($oldplacementsecret) && (strlen($oldplacementsecret) > 1)) {
+ $plaintext = $oldplacementsecret . $suffix;
+ $hashsig = hash('sha256', $plaintext, false);
+}
+
+if ($hashsig != $signature) {
+ do_error("Invalid sourcedid");
+}
+
+// Check the OAuth Signature
+$oauth_secret = $typeconfig["password"];
+$oauth_consumer_key = $typeconfig["resourcekey"];
+if (! isset($oauth_secret)) {
+ do_error("Not permitted (5)");
+}
+if (! isset($oauth_consumer_key)) {
+ do_error("Not permitted (6)");
+}
+
+// Verify the message signature
+$store = new TrivialOAuthDataStore();
+$store->add_consumer($oauth_consumer_key, $oauth_secret);
+
+$server = new OAuthServer($store);
+
+$method = new OAuthSignatureMethod_HMAC_SHA1();
+$server->add_signature_method($method);
+$request = OAuthRequest::from_request();
+
+$basestring = $request->get_signature_base_string();
+try {
+ $server->verify_request($request);
+} catch (Exception $e) {
+ do_error($e->getMessage());
+}
+
+if (! $course = $DB->get_record('course', array('id'=>$basiclti->course))) {
+ do_error("Could not retrieve course");
+}
+
+// TODO: Check that user is in course
+
+if (! $cm = get_coursemodule_from_instance("lti", $basiclti->id, $course->id)) {
+ do_error("Course Module ID was incorrect");
+}
+
+// Lets store the grade
+require_once($CFG->libdir.'/gradelib.php');
+
+// Beginning of actual grade processing
+if ($message_type == "basicoutcome") {
+ $source = 'mod/lti';
+ $courseid = $course->id;
+ $itemtype = 'mod';
+ $itemmodule = 'lti';
+ $iteminstance = $basiclti->id;
+
+ if ($lti_message_type == "basic-lis-readresult") {
+ unset($grade);
+ $thegrade = grade_get_grades($courseid, $itemtype, $itemmodule, $iteminstance, $userid);
+ // print_r($thegrade->items[0]->grades);
+ if (isset($thegrade) && is_array($thegrade->items[0]->grades)) {
+ foreach ($thegrade->items[0]->grades as $agrade) {
+ $grade = $agrade->grade;
+ break;
+ }
+ }
+ if (! isset($grade)) {
+ do_error("Unable to read grade");
+ }
+
+ $result = " <result>\n" .
+ " <resultscore>\n" .
+ " <textstring>" .
+ htmlspecialchars($grade/100.0) .
+ "</textstring>\n" .
+ " </resultscore>\n" .
+ " </result>\n";
+ print message_response('Success', 'Status', false, "Grade read", $result);
+ exit();
+ }
+
+ if ($lti_message_type == "basic-lis-deleteresult") {
+ $params = array();
+ $params['itemname'] = $basiclti->name;
+
+ $grade = new stdClass();
+ $grade->userid = $userid;
+ $grade->rawgrade = null;
+
+ grade_update($source, $courseid, $itemtype, $itemmodule, $iteminstance, 0, $grade, array('deleted'=>1));
+ } else {
+ if (isset($_REQUEST['result_resultscore_textstring'])) {
+ $gradeval = floatval($_REQUEST['result_resultscore_textstring']);
+ if ($gradeval <= 1.0 && $gradeval >= 0.0) {
+ $gradeval = $gradeval * 100.0;
+ }
+ } else {
+ do_error('Missing Grade');
+ }
+ $params = array();
+ $params['itemname'] = $basiclti->name;
+
+ $grade = new stdClass();
+ $grade->userid = $userid;
+ $grade->rawgrade = $gradeval;
+
+ grade_update($source, $courseid, $itemtype, $itemmodule, $iteminstance, 0, $grade, $params);
+ }
+
+ print message_response('Success', 'Status', 'fullsuccess', 'Grade updated');
+
+} else if ($lti_message_type == "basic-lti-loadsetting") {
+ $xml = " <setting>\n" .
+ " <value>".htmlspecialchars($basiclti->setting)."</value>\n" .
+ " </setting>\n";
+ print message_response('Success', 'Status', 'fullsuccess', 'Setting retrieved', $xml);
+} else if ($lti_message_type == "basic-lti-savesetting") {
+ $setting = $_REQUEST['setting'];
+ if (! isset($setting)) {
+ do_error('Missing setting value');
+ }
+ $record = $DB->get_record('lti', array('id'=>$basiclti->id));
+ $record->setting = $setting;
+ $success = $DB->update_record('lti', $record);
+ if ($success) {
+ print message_response('Success', 'Status', 'fullsuccess', 'Setting updated');
+ } else {
+ do_error("Error updating error");
+ }
+} else if ($lti_message_type == "basic-lti-deletesetting") {
+ $record = $DB->get_record('lti', array('id'=>$basiclti->id));
+ $record->setting = '';
+ $success = $DB->update_record('lti', $record);
+ if ($success) {
+ print message_response('Success', 'Status', 'fullsuccess', 'Setting deleted');
+ } else {
+ do_error("Error updating error");
+ }
+} else if ($message_type == "roster") {
+ if (! $course = $DB->get_record('course', array('id'=>$basiclti->course))) {
+ do_error("Could not retrieve course");
+ }
+ if (! $context = get_context_instance(CONTEXT_COURSE, $course->id)) {
+ do_error("Could not retrieve context");
+ }
+ $sql = 'SELECT u.id, u.username, u.firstname, u.lastname, u.email, ro.shortname
+ FROM '.$CFG->prefix.'role_assignments ra
+ JOIN '.$CFG->prefix.'user AS u ON ra.userid = u.id
+ JOIN '.$CFG->prefix.'role ro ON ra.roleid = ro.id
+ WHERE ra.contextid = '.$context->id;
+ $userlist = $DB->get_recordset_sql($sql);
+ $xml = " <memberships>\n";
+ foreach ($userlist as $user) {
+ $role = "Learner";
+ if ($user->shortname == 'editingteacher' || $user->shortname == 'admin') {
+ $role = 'Instructor';
+ }
+ $userxml = " <member>\n".
+ " <user_id>".htmlspecialchars($user->id)."</user_id>\n".
+ " <roles>$role</roles>\n";
+ if ($typeconfig["sendname"] == 1 ||
+ ($typeconfig["sendname"] == 2 && $basiclti->instructorchoicesendname == 1)) {
+ if (isset($user->firstname)) {
+ $userxml .= " <person_name_given>".htmlspecialchars($user->firstname)."</person_name_given>\n";
+ }
+ if (isset($user->lastname)) {
+ $userxml .= " <person_name_family>".htmlspecialchars($user->lastname)."</person_name_family>\n";
+ }
+ }
+ if ($typeconfig["sendemailaddr"] == 1 ||
+ ($typeconfig["sendemailaddr"] == 2 && $basiclti->instructorchoicesendemailaddr == 1)) {
+ if (isset($user->email)) {
+ $userxml .= " <person_contact_email_primary>".htmlspecialchars($user->email)."</person_contact_email_primary>\n";
+ }
+ }
+ $placementsecret = $basiclti->placementsecret;
+ if (isset($placementsecret)) {
+ $suffix = ':::' . $user->id . ':::' . $basiclti->id;
+ $plaintext = $placementsecret . $suffix;
+ $hashsig = hash('sha256', $plaintext, false);
+ $sourcedid = $hashsig . $suffix;
+ }
+ if ($typeconfig["acceptgrades"] == 1 ||
+ ($typeconfig["acceptgrades"] == 2 && $basiclti->instructorchoiceacceptgrades == 1)) {
+ if (isset($sourcedid)) {
+ $userxml .= " <lis_result_sourcedid>".htmlspecialchars($sourcedid)."</lis_result_sourcedid>\n";
+ }
+ }
+ $userxml .= " </member>\n";
+ $xml .= $userxml;
+ }
+ $xml .= " </memberships>\n";
+ print message_response('Success', 'Status', 'fullsuccess', 'Roster retreived', $xml);
+
+}
+
View
621 mod/lti/service.php
@@ -1,391 +1,230 @@
-<?php
-// This file is part of BasicLTI4Moodle
-//
-// BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)
-// consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web
-// based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI
-// specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS
-// are already supporting or going to support BasicLTI. This project Implements the consumer
-// for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.
-// BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem
-// at the GESSI research group at UPC.
-// SimpleLTI consumer for Moodle is an implementation of the early specification of LTI
-// by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a
-// Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.
-//
-// BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis
-// of the Universitat Politecnica de Catalunya http://www.upc.edu
-// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This file contains all necessary code to support basiclti services
- * like outcomes and roster access.
- *
- * @package lti
- * @copyright 2009 Marc Alier, Jordi Piguillem, Nikolas Galanis
- * marc.alier@upc.edu
- * @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu
- *
- * @author Marc Alier
- * @author Jordi Piguillem
- * @author Nikolas Galanis
- * @author Charles Severance
- *
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require_once("../../config.php");
-require_once($CFG->dirroot.'/mod/lti/lib.php');
-require_once($CFG->dirroot.'/mod/lti/locallib.php');
-require_once($CFG->dirroot.'/mod/lti/OAuth.php');
-require_once($CFG->dirroot.'/mod/lti/TrivialStore.php');
-
-error_reporting(E_ALL & ~E_NOTICE);
-ini_set("display_errors", 1);
-
-$PAGE->set_context(get_context_instance(CONTEXT_SYSTEM));
-$PAGE->set_url('/mod/lti/service.php');
-$PAGE->set_pagetype('admin-setting-' . $section);
-$PAGE->set_pagelayout('admin');
-$PAGE->navigation->clear_cache();
-
-function message_response($major, $severity, $minor=false, $message=false, $xml=false) {
- $lti_message_type = $_REQUEST['lti_message_type'];
- $retval = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"."\n" .
- "<message_response>\n" .
- " <lti_message_type>$lti_message_type</lti_message_type>\n" .
- " <statusinfo>\n" .
- " <codemajor>$major</codemajor>\n" .
- " <severity>$severity</severity>\n";
- if (! $codeminor === false) {
- $retval = $retval . " <codeminor>$minor</codeminor>\n";
- }
- $retval = $retval .
- " <description>$message</description>\n" .
- " </statusinfo>\n";
- if (! $xml === false) {
- $retval = $retval . $xml;
- }
- $retval = $retval . "</message_response>\n";
- return $retval;
-}
-
-function do_error($message) {
- print message_response('Fail', 'Error', false, $message);
- exit();
-}
-
-$lti_version = $_REQUEST['lti_version'];
-if ($lti_version != "LTI-1p0") {
- do_error("Improperly formed message: wrong lti version: ".$lti_version);
-}
-
-$lti_message_type = $_REQUEST['lti_message_type'];
-if (! isset($lti_message_type)) {
- do_error("Improperly formed message: no lti_message_type parameter");
-}
-
-$message_type = false;
-if ($lti_message_type == "basic-lis-replaceresult" ||
- $lti_message_type == "basic-lis-createresult" ||
- $lti_message_type == "basic-lis-updateresult" ||
- $lti_message_type == "basic-lis-deleteresult" ||
- $lti_message_type == "basic-lis-readresult") {
- $sourcedid = $_REQUEST['sourcedid'];
- $message_type = "basicoutcome";
-} else if ($lti_message_type == "basic-lti-loadsetting" ||
- $lti_message_type == "basic-lti-savesetting" ||
- $lti_message_type == "basic-lti-deletesetting") {
- $sourcedid = $_REQUEST['id'];
- $message_type = "toolsetting";
-} else if ($lti_message_type == "basic-lis-readmembershipsforcontext") {
- $sourcedid = $_REQUEST['id'];
- $message_type = "roster";
-}
-
-if ($message_type == false) {
- do_error("Illegal lti_message_type");
-}
-
-if (!isset($sourcedid)) {
- do_error("sourcedid missing");
-}
-// Truncate to maximum length
-$sourcedid = substr($sourcedid, 0, 2048);
-
-try {
- $info = explode(':::', $sourcedid);
- if (! is_array($info)) {
- do_error("Bad sourcedid (1)");
- }
- $signature = $info[0];
- $userid = intval($info[1]);
- $placement = $info[2];
-} catch (Exception $e) {
- do_error("Bad sourcedid (2)");
-}
-
-if (isset($signature) && isset($userid) && isset($placement)) {
- // OK
-} else {
- do_error("Bad sourcedid (3)");
-}
-
-// Retrieve the Basic LTI placement
-if (! $basiclti = $DB->get_record('lti', array('id'=>$placement))) {
- do_error("Bad sourcedid (4)");
-}
-
-$basiclti_types_config = (object)$basiclti_types_config;
-
-$typeconfig = lti_get_type_config($basiclti->typeid);
-
-if (isset($typeconfig) && isset($typeconfig['password'])) {
- // OK
-} else {
- do_error("Unable to load type");
-}
-
-if ($message_type == "basicoutcome") {
- if ($typeconfig["acceptgrades"] == 1 ||
- ($typeconfig["acceptgrades"] == 2 && $basiclti->instructorchoiceacceptgrades == 1)) {
- // The placement is configured to accept grades
- } else {
- do_error("Not permitted (1)");
- }
-} else if ($message_type == "toolsetting") {
- if ($typeconfig["allowsetting"] == 1 ||
- ($typeconfig["allowsetting"] == 2 && $basiclti->instructorchoiceallowsetting == 1)) {
- // OK
- } else {
- do_error("Not permitted (2)");
- }
-} else if ($message_type == "roster") {
- if ($typeconfig["allowroster"] == 1 ||
- ($typeconfig["allowroster"] == 2 && $basiclti->instructorchoiceallowroster == 1)) {
- // OK
- } else {
- do_error("Not permitted (3)");
- }
-}
-
-// Retrieve the secret we use to sign lis_result_sourcedid
-$placementsecret = $basiclti->placementsecret;
-$oldplacementsecret = $basiclti->oldplacementsecret;
-if (! isset($placementsecret)) {
- do_error("Not permitted (4)");
-}
-
-$suffix = ':::' . $userid . ':::' . $placement;
-$plaintext = $placementsecret . $suffix;
-$hashsig = hash('sha256', $plaintext, false);
-if (($hashsig != $signature) && isset($oldplacementsecret) && (strlen($oldplacementsecret) > 1)) {
- $plaintext = $oldplacementsecret . $suffix;
- $hashsig = hash('sha256', $plaintext, false);
-}
-
-if ($hashsig != $signature) {
- do_error("Invalid sourcedid");
-}
-
-// Check the OAuth Signature
-$oauth_secret = $typeconfig["password"];
-$oauth_consumer_key = $typeconfig["resourcekey"];
-if (! isset($oauth_secret)) {
- do_error("Not permitted (5)");
-}
-if (! isset($oauth_consumer_key)) {
- do_error("Not permitted (6)");
-}
-
-// Verify the message signature
-$store = new TrivialOAuthDataStore();
-$store->add_consumer($oauth_consumer_key, $oauth_secret);
-
-$server = new OAuthServer($store);
-
-$method = new OAuthSignatureMethod_HMAC_SHA1();
-$server->add_signature_method($method);
-$request = OAuthRequest::from_request();
-
-$basestring = $request->get_signature_base_string();
-try {
- $server->verify_request($request);
-} catch (Exception $e) {
- do_error($e->getMessage());
-}
-
-if (! $course = $DB->get_record('course', array('id'=>$basiclti->course))) {
- do_error("Could not retrieve course");
-}
-
-// TODO: Check that user is in course
-
-if (! $cm = get_coursemodule_from_instance("lti", $basiclti->id, $course->id)) {
- do_error("Course Module ID was incorrect");
-}
-
-// Lets store the grade
-require_once($CFG->libdir.'/gradelib.php');
-
-// Beginning of actual grade processing
-if ($message_type == "basicoutcome") {
- $source = 'mod/lti';
- $courseid = $course->id;
- $itemtype = 'mod';
- $itemmodule = 'lti';
- $iteminstance = $basiclti->id;
-
- if ($lti_message_type == "basic-lis-readresult") {
- unset($grade);
- $thegrade = grade_get_grades($courseid, $itemtype, $itemmodule, $iteminstance, $userid);
- // print_r($thegrade->items[0]->grades);
- if (isset($thegrade) && is_array($thegrade->items[0]->grades)) {
- foreach ($thegrade->items[0]->grades as $agrade) {
- $grade = $agrade->grade;
- break;
- }
- }
- if (! isset($grade)) {
- do_error("Unable to read grade");
- }
-
- $result = " <result>\n" .
- " <resultscore>\n" .
- " <textstring>" .
- htmlspecialchars($grade/100.0) .
- "</textstring>\n" .
- " </resultscore>\n" .
- " </result>\n";
- print message_response('Success', 'Status', false, "Grade read", $result);
- exit();
- }
-
- if ($lti_message_type == "basic-lis-deleteresult") {
- $params = array();
- $params['itemname'] = $basiclti->name;
-
- $grade = new stdClass();
- $grade->userid = $userid;
- $grade->rawgrade = null;
-
- grade_update($source, $courseid, $itemtype, $itemmodule, $iteminstance, 0, $grade, array('deleted'=>1));
- } else {
- if (isset($_REQUEST['result_resultscore_textstring'])) {
- $gradeval = floatval($_REQUEST['result_resultscore_textstring']);
- if ($gradeval <= 1.0 && $gradeval >= 0.0) {
- $gradeval = $gradeval * 100.0;
- }
- } else {
- do_error('Missing Grade');
- }
- $params = array();
- $params['itemname'] = $basiclti->name;
-
- $grade = new stdClass();
- $grade->userid = $userid;
- $grade->rawgrade = $gradeval;
-
- grade_update($source, $courseid, $itemtype, $itemmodule, $iteminstance, 0, $grade, $params);
- }
-
- print message_response('Success', 'Status', 'fullsuccess', 'Grade updated');
-
-} else if ($lti_message_type == "basic-lti-loadsetting") {
- $xml = " <setting>\n" .
- " <value>".htmlspecialchars($basiclti->setting)."</value>\n" .
- " </setting>\n";
- print message_response('Success', 'Status', 'fullsuccess', 'Setting retrieved', $xml);
-} else if ($lti_message_type == "basic-lti-savesetting") {
- $setting = $_REQUEST['setting'];
- if (! isset($setting)) {
- do_error('Missing setting value');
- }
- $record = $DB->get_record('lti', array('id'=>$basiclti->id));
- $record->setting = $setting;
- $success = $DB->update_record('lti', $record);
- if ($success) {
- print message_response('Success', 'Status', 'fullsuccess', 'Setting updated');
- } else {
- do_error("Error updating error");
- }
-} else if ($lti_message_type == "basic-lti-deletesetting") {
- $record = $DB->get_record('lti', array('id'=>$basiclti->id));
- $record->setting = '';
- $success = $DB->update_record('lti', $record);
- if ($success) {
- print message_response('Success', 'Status', 'fullsuccess', 'Setting deleted');
- } else {
- do_error("Error updating error");
- }
-} else if ($message_type == "roster") {
- if (! $course = $DB->get_record('course', array('id'=>$basiclti->course))) {
- do_error("Could not retrieve course");
- }
- if (! $context = get_context_instance(CONTEXT_COURSE, $course->id)) {
- do_error("Could not retrieve context");
- }
- $sql = 'SELECT u.id, u.username, u.firstname, u.lastname, u.email, ro.shortname
- FROM '.$CFG->prefix.'role_assignments ra
- JOIN '.$CFG->prefix.'user AS u ON ra.userid = u.id
- JOIN '.$CFG->prefix.'role ro ON ra.roleid = ro.id
- WHERE ra.contextid = '.$context->id;
- $userlist = $DB->get_recordset_sql($sql);
- $xml = " <memberships>\n";
- foreach ($userlist as $user) {
- $role = "Learner";
- if ($user->shortname == 'editingteacher' || $user->shortname == 'admin') {
- $role = 'Instructor';
- }
- $userxml = " <member>\n".
- " <user_id>".htmlspecialchars($user->id)."</user_id>\n".
- " <roles>$role</roles>\n";
- if ($typeconfig["sendname"] == 1 ||
- ($typeconfig["sendname"] == 2 && $basiclti->instructorchoicesendname == 1)) {
- if (isset($user->firstname)) {
- $userxml .= " <person_name_given>".htmlspecialchars($user->firstname)."</person_name_given>\n";
- }
- if (isset($user->lastname)) {
- $userxml .= " <person_name_family>".htmlspecialchars($user->lastname)."</person_name_family>\n";
- }
- }
- if ($typeconfig["sendemailaddr"] == 1 ||
- ($typeconfig["sendemailaddr"] == 2 && $basiclti->instructorchoicesendemailaddr == 1)) {
- if (isset($user->email)) {
- $userxml .= " <person_contact_email_primary>".htmlspecialchars($user->email)."</person_contact_email_primary>\n";
- }
- }
- $placementsecret = $basiclti->placementsecret;
- if (isset($placementsecret)) {
- $suffix = ':::' . $user->id . ':::' . $basiclti->id;
- $plaintext = $placementsecret . $suffix;
- $hashsig = hash('sha256', $plaintext, false);
- $sourcedid = $hashsig . $suffix;
- }
- if ($typeconfig["acceptgrades"] == 1 ||
- ($typeconfig["acceptgrades"] == 2 && $basiclti->instructorchoiceacceptgrades == 1)) {
- if (isset($sourcedid)) {
- $userxml .= " <lis_result_sourcedid>".htmlspecialchars($sourcedid)."</lis_result_sourcedid>\n";
- }
- }
- $userxml .= " </member>\n";
- $xml .= $userxml;
- }
- $xml .= " </memberships>\n";
- print message_response('Success', 'Status', 'fullsuccess', 'Roster retreived', $xml);
-
-}
-
+<?php
+require_once("../../config.php");
+require_once($CFG->dirroot.'/mod/lti/OAuthBody.php');
+require_once($CFG->dirroot.'/mod/lti/locallib.php');
+
+define('LTI_ITEM_TYPE', 'mod');
+define('LTI_ITEM_MODULE', 'lti');
+define('LTI_SOURCE', 'mod/lti');
+
+function lti_get_response_xml($codemajor, $description, $messageref, $messagetype){
+ $xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><imsx_POXEnvelopeResponse />');
+ $xml->addAttribute('xmlns', 'http://www.imsglobal.org/lis/oms1p0/pox');
+
+ $headerinfo = $xml->addChild('imsx_POXHeader')
+ ->addChild('imsx_POXResponseHeaderInfo');
+
+ $headerinfo->addChild('imsx_version', 'V1.0');
+ $headerinfo->addChild('imsx_messageIdentifier', (string)mt_rand());
+
+ $statusinfo = $headerinfo->addChild('imsx_statusInfo');
+ $statusinfo->addchild('imsx_codeMajor', $codemajor);
+ $statusinfo->addChild('imsx_severity', 'status');
+ $statusinfo->addChild('imsx_description', $description);
+ $statusinfo->addChild('imsx_messageRefIdentifier', $messageref);
+
+ $xml->addChild('imsx_POXBody')
+ ->addChild($messagetype);
+
+ return $xml;
+}
+
+function lti_parse_message_id($xml){
+ $node = $xml->imsx_POXHeader->imsx_POXRequestHeaderInfo->imsx_messageIdentifier;
+ $messageid = (string)$node;
+
+ return $messageid;
+}
+
+function lti_parse_grade_replace_message($xml){
+ $node = $xml->imsx_POXBody->replaceResultRequest->resultRecord->sourcedGUID->sourcedId;
+ $resultjson = json_decode((string)$node);
+
+ $node = $xml->imsx_POXBody->replaceResultRequest->resultRecord->result->resultScore->textString;
+ $grade = floatval((string)$node);
+
+ $parsed = new stdClass();
+ $parsed->gradeval = $grade * 100;
+ $parsed->instanceid = $resultjson->data->instanceid;
+ $parsed->userid = $resultjson->data->userid;
+ $parsed->messageid = lti_parse_message_id($xml);
+
+ return $parsed;
+}
+
+function lti_parse_grade_read_message($xml){
+ $node = $xml->imsx_POXBody->readResultRequest->resultRecord->sourcedGUID->sourcedId;
+ $resultjson = json_decode((string)$node);
+
+ $parsed = new stdClass();
+ $parsed->instanceid = $resultjson->data->instanceid;
+ $parsed->userid = $resultjson->data->userid;
+ $parsed->messageid = lti_parse_message_id($xml);
+
+ return $parsed;
+}
+
+function lti_parse_grade_delete_message($xml){
+ $node = $xml->imsx_POXBody->deleteResultRequest->resultRecord->sourcedGUID->sourcedId;
+ $resultjson = json_decode((string)$node);
+
+ $parsed = new stdClass();
+ $parsed->instanceid = $resultjson->data->instanceid;
+ $parsed->userid = $resultjson->data->userid;
+ $parsed->messageid = lti_parse_message_id($xml);
+
+ return $parsed;
+}
+
+function lti_update_grade($ltiinstance, $userid, $gradeval){
+ global $CFG;
+ require_once($CFG->libdir . '/gradelib.php');
+
+ $params = array();
+ $params['itemname'] = $ltiinstance->name;
+
+ $grade = new stdClass();
+ $grade->userid = $userid;
+ $grade->rawgrade = $gradeval;
+
+ $status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade, $params);
+
+ return $status == GRADE_UPDATE_OK;
+}
+
+function lti_read_grade($ltiinstance, $userid){
+ global $CFG;
+ require_once($CFG->libdir . '/gradelib.php');
+
+ $grades = grade_get_grades($ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, $userid);
+
+ if (isset($grades) && is_array($grades->items[0]->grades)) {
+ foreach ($grades->items[0]->grades as $agrade) {
+ $grade = $agrade->grade;
+ break;
+ }
+ }
+
+ if(isset($grade)){
+ return $grade;
+ }
+}
+
+function lti_delete_grade($ltiinstance, $userid){
+ $grade = new stdClass();
+ $grade->userid = $userid;
+ $grade->rawgrade = null;
+
+ $status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade, array('deleted'=>1));
+
+ return $status == GRADE_UPDATE_OK || $status == GRADE_UPDATE_ITEM_DELETED; //grade_update seems to return ok now, but could reasonably return deleted in the future
+}
+
+function lti_verify_message($ltiinstance){
+ //Use the key / secret configured on the tool, or look it up from the admin config
+ if(empty($ltiinstance->resourcekey) || empty($ltiinstance->password)){
+ if($ltiinstance->typeid){
+ $typeid = $ltiinstance->typeid;
+ } else {
+ $tool = lti_get_tool_by_url_match($ltiinstance->toolurl);
+
+ if(!$tool){
+ throw new Exception('Tool configuration not found for tool instance ' . $ltiinstance->id);
+ }
+
+ $typeid = $tool->id;
+ }
+
+ $typeconfig = lti_get_type_config($typeid);//Consider only fetching the 2 necessary settings here
+
+ $key = $typeconfig['resourcekey'];
+ $secret = $typeconfig['password'];
+ } else {
+ $key = $ltiinstance->resourcekey;
+ $secret = $ltiinstance->password;
+ }
+
+ handleOAuthBodyPOST($key, $secret);
+}
+
+$xmlfragment = file_get_contents("php://input");
+$xml = new SimpleXMLElement($xmlfragment);
+
+$body = $xml->imsx_POXBody;
+foreach($body->children() as $child){
+ $messagetype = $child->getName();
+}
+
+switch($messagetype){
+ case 'replaceResultRequest':
+ $parsed = lti_parse_grade_replace_message($xml);
+
+ $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
+
+ lti_verify_message($ltiinstance);
+
+ $gradestatus = lti_update_grade($ltiinstance, $parsed->userid, $parsed->gradeval);
+
+ $responsexml = lti_get_response_xml(
+ $gradestatus ? 'success' : 'error',
+ 'Grade replace response',
+ $parsed->messageid,
+ 'replaceResultResponse'
+ );
+
+ echo $responsexml->asXML();
+
+ break;
+
+ case 'readResultRequest':
+ $parsed = lti_parse_grade_read_message($xml);
+
+ $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
+
+ lti_verify_message($ltiinstance);
+
+ $grade = lti_read_grade($ltiinstance, $parsed->userid);
+
+ $responsexml = lti_get_response_xml(
+ isset($grade) ? 'success' : 'error',
+ 'Result read',
+ $parsed->messageid,
+ 'readResultResponse'
+ );
+
+ $node = $responsexml->imsx_POXBody->readResultResponse;
+ $node->addChild('result')
+ ->addChild('resultScore')
+ ->addChild('textString', isset($grade) ? $grade : '');
+
+ echo $responsexml->asXML();
+
+ break;
+
+ case 'deleteResultRequest':
+ $parsed = lti_parse_grade_delete_message($xml);
+
+ $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
+
+ lti_verify_message($ltiinstance);
+
+ $gradestatus = lti_delete_grade($ltiinstance, $parsed->userid);
+
+ $responsexml = lti_get_response_xml(
+ $gradestatus ? 'success' : 'error',
+ 'Grade delete request',
+ $parsed->messageid,
+ 'deleteResultResponse'
+ );
+
+ echo $responsexml->asXML();
+
+ break;
+}
+
+
+//echo print_r(apache_request_headers(), true);
+
+//echo '<br />';
+
+//echo file_get_contents("php://input");

0 comments on commit b9b2e7b

Please sign in to comment.
Something went wrong with that request. Please try again.