2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * LTI web service endpoints
21 * @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 * @author Chris Scribner
26 define('NO_DEBUG_DISPLAY', true);
27 define('NO_MOODLE_COOKIES', true);
29 require_once(__DIR__
. "/../../config.php");
30 require_once($CFG->dirroot
.'/mod/lti/locallib.php');
31 require_once($CFG->dirroot
.'/mod/lti/servicelib.php');
33 // TODO: Switch to core oauthlib once implemented - MDL-30149.
34 use mod_lti\service_exception_handler
;
35 use moodle\mod\lti
as lti
;
37 $rawbody = file_get_contents("php://input");
39 $logrequests = lti_should_log_request($rawbody);
40 $errorhandler = new service_exception_handler($logrequests);
42 // Register our own error handler so we can always send valid XML response.
43 set_exception_handler(array($errorhandler, 'handle'));
46 lti_log_request($rawbody);
49 foreach (lti\OAuthUtil
::get_headers() as $name => $value) {
50 if ($name === 'Authorization') {
51 // TODO: Switch to core oauthlib once implemented - MDL-30149.
52 $oauthparams = lti\OAuthUtil
::split_header($value);
54 $consumerkey = $oauthparams['oauth_consumer_key'];
59 if (empty($consumerkey)) {
60 throw new Exception('Consumer key is missing.');
63 $sharedsecret = lti_verify_message($consumerkey, lti_get_shared_secrets_by_key($consumerkey), $rawbody);
65 if ($sharedsecret === false) {
66 throw new Exception('Message signature not valid');
69 // TODO MDL-46023 Replace this code with a call to the new library.
70 $origentity = libxml_disable_entity_loader(true);
71 $xml = simplexml_load_string($rawbody);
73 libxml_disable_entity_loader($origentity);
74 throw new Exception('Invalid XML content');
76 libxml_disable_entity_loader($origentity);
78 $body = $xml->imsx_POXBody
;
79 foreach ($body->children() as $child) {
80 $messagetype = $child->getName();
83 // We know more about the message, update error handler to send better errors.
84 $errorhandler->set_message_id(lti_parse_message_id($xml));
85 $errorhandler->set_message_type($messagetype);
87 switch ($messagetype) {
88 case 'replaceResultRequest':
89 $parsed = lti_parse_grade_replace_message($xml);
91 $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid
));
93 if (!lti_accepts_grades($ltiinstance)) {
94 throw new Exception('Tool does not accept grades');
97 lti_verify_sourcedid($ltiinstance, $parsed);
98 lti_set_session_user($parsed->userid
);
100 $gradestatus = lti_update_grade($ltiinstance, $parsed->userid
, $parsed->launchid
, $parsed->gradeval
);
103 throw new Exception('Grade replace response');
106 $responsexml = lti_get_response_xml(
108 'Grade replace response',
110 'replaceResultResponse'
113 echo $responsexml->asXML();
117 case 'readResultRequest':
118 $parsed = lti_parse_grade_read_message($xml);
120 $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid
));
122 if (!lti_accepts_grades($ltiinstance)) {
123 throw new Exception('Tool does not accept grades');
126 // Getting the grade requires the context is set.
127 $context = context_course
::instance($ltiinstance->course
);
128 $PAGE->set_context($context);
130 lti_verify_sourcedid($ltiinstance, $parsed);
132 $grade = lti_read_grade($ltiinstance, $parsed->userid
);
134 $responsexml = lti_get_response_xml(
135 'success', // Empty grade is also 'success'.
141 $node = $responsexml->imsx_POXBody
->readResultResponse
;
142 $node = $node->addChild('result')->addChild('resultScore');
143 $node->addChild('language', 'en');
144 $node->addChild('textString', isset($grade) ?
$grade : '');
146 echo $responsexml->asXML();
150 case 'deleteResultRequest':
151 $parsed = lti_parse_grade_delete_message($xml);
153 $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid
));
155 if (!lti_accepts_grades($ltiinstance)) {
156 throw new Exception('Tool does not accept grades');
159 lti_verify_sourcedid($ltiinstance, $parsed);
160 lti_set_session_user($parsed->userid
);
162 $gradestatus = lti_delete_grade($ltiinstance, $parsed->userid
);
165 throw new Exception('Grade delete request');
168 $responsexml = lti_get_response_xml(
170 'Grade delete request',
172 'deleteResultResponse'
175 echo $responsexml->asXML();
180 // Fire an event if we get a web service request which we don't support directly.
181 // This will allow others to extend the LTI services, which I expect to be a common
182 // use case, at least until the spec matures.
183 $data = new stdClass();
184 $data->body
= $rawbody;
186 $data->messageid
= lti_parse_message_id($xml);
187 $data->messagetype
= $messagetype;
188 $data->consumerkey
= $consumerkey;
189 $data->sharedsecret
= $sharedsecret;
190 $eventdata = array();
191 $eventdata['other'] = array();
192 $eventdata['other']['messageid'] = $data->messageid
;
193 $eventdata['other']['messagetype'] = $messagetype;
194 $eventdata['other']['consumerkey'] = $consumerkey;
196 // Before firing the event, allow subplugins a chance to handle.
197 if (lti_extend_lti_services($data)) {
201 // If an event handler handles the web service, it should set this global to true
202 // So this code knows whether to send an "operation not supported" or not.
203 global $ltiwebservicehandled;
204 $ltiwebservicehandled = false;
207 $event = \mod_lti\event\unknown_service_api_called
::create($eventdata);
208 $event->set_message_data($data);
210 } catch (Exception
$e) {
211 $ltiwebservicehandled = false;
214 if (!$ltiwebservicehandled) {
215 $responsexml = lti_get_response_xml(
218 lti_parse_message_id($xml),
222 echo $responsexml->asXML();