on-demand release 4.2dev+
[moodle.git] / mod / lti / service.php
CommitLineData
b9b2e7bb 1<?php
61eb12d4
CS
2// This file is part of Moodle - http://moodle.org/
3//
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.
8//
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.
13//
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/>.
16
17/**
8f45215d 18 * LTI web service endpoints
61eb12d4 19 *
2b17ec3d 20 * @package mod_lti
8f45215d 21 * @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
61eb12d4 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8f45215d 23 * @author Chris Scribner
61eb12d4
CS
24 */
25
4d7a2c9e 26define('NO_DEBUG_DISPLAY', true);
e3f69b58 27define('NO_MOODLE_COOKIES', true);
4d7a2c9e 28
1fcf0ca8 29require_once(__DIR__ . "/../../config.php");
b9b2e7bb 30require_once($CFG->dirroot.'/mod/lti/locallib.php');
996b0fd9 31require_once($CFG->dirroot.'/mod/lti/servicelib.php');
b9b2e7bb 32
e3f69b58 33// TODO: Switch to core oauthlib once implemented - MDL-30149.
00e27060 34use mod_lti\service_exception_handler;
020eea1b 35use moodle\mod\lti as lti;
27cbb596 36use ltiservice_basicoutcomes\local\service\basicoutcomes;
020eea1b 37
996b0fd9 38$rawbody = file_get_contents("php://input");
020eea1b 39
00e27060
MN
40$logrequests = lti_should_log_request($rawbody);
41$errorhandler = new service_exception_handler($logrequests);
42
43// Register our own error handler so we can always send valid XML response.
44set_exception_handler(array($errorhandler, 'handle'));
45
46if ($logrequests) {
8fa50fdd
MN
47 lti_log_request($rawbody);
48}
49
27cbb596
SV
50$ok = true;
51$type = null;
52$toolproxy = false;
e27cb316 53
27cbb596
SV
54$consumerkey = lti\get_oauth_key_from_headers(null, array(basicoutcomes::SCOPE_BASIC_OUTCOMES));
55if ($consumerkey === false) {
56 throw new Exception('Missing or invalid consumer key or access token.');
57} else if (is_string($consumerkey)) {
58 $toolproxy = lti_get_tool_proxy_from_guid($consumerkey);
59 if ($toolproxy !== false) {
60 $secrets = array($toolproxy->secret);
61 } else if (!empty($tool)) {
62 $secrets = array($typeconfig['password']);
63 } else {
64 $secrets = lti_get_shared_secrets_by_key($consumerkey);
65 }
66 $sharedsecret = lti_verify_message($consumerkey, lti_get_shared_secrets_by_key($consumerkey), $rawbody);
67 if ($sharedsecret === false) {
68 throw new Exception('Message signature not valid');
020eea1b 69 }
020eea1b
CS
70}
71
78ed99ec 72// TODO MDL-46023 Replace this code with a call to the new library.
1e8375d8 73$origentity = lti_libxml_disable_entity_loader(true);
78ed99ec
FM
74$xml = simplexml_load_string($rawbody);
75if (!$xml) {
1e8375d8 76 lti_libxml_disable_entity_loader($origentity);
78ed99ec
FM
77 throw new Exception('Invalid XML content');
78}
1e8375d8 79lti_libxml_disable_entity_loader($origentity);
b9b2e7bb
CS
80
81$body = $xml->imsx_POXBody;
ea04a9f9 82foreach ($body->children() as $child) {
b9b2e7bb
CS
83 $messagetype = $child->getName();
84}
85
00e27060
MN
86// We know more about the message, update error handler to send better errors.
87$errorhandler->set_message_id(lti_parse_message_id($xml));
88$errorhandler->set_message_type($messagetype);
89
ea04a9f9 90switch ($messagetype) {
b9b2e7bb 91 case 'replaceResultRequest':
00e27060 92 $parsed = lti_parse_grade_replace_message($xml);
e27cb316 93
b9b2e7bb 94 $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
e27cb316 95
8fa50fdd
MN
96 if (!lti_accepts_grades($ltiinstance)) {
97 throw new Exception('Tool does not accept grades');
98 }
99
996b0fd9 100 lti_verify_sourcedid($ltiinstance, $parsed);
8fa50fdd 101 lti_set_session_user($parsed->userid);
e27cb316 102
f4f711d7 103 $gradestatus = lti_update_grade($ltiinstance, $parsed->userid, $parsed->launchid, $parsed->gradeval);
e27cb316 104
00e27060
MN
105 if (!$gradestatus) {
106 throw new Exception('Grade replace response');
107 }
108
b9b2e7bb 109 $responsexml = lti_get_response_xml(
00e27060 110 'success',
b9b2e7bb
CS
111 'Grade replace response',
112 $parsed->messageid,
113 'replaceResultResponse'
114 );
e27cb316 115
b9b2e7bb 116 echo $responsexml->asXML();
e27cb316 117
b9b2e7bb 118 break;
e27cb316 119
b9b2e7bb
CS
120 case 'readResultRequest':
121 $parsed = lti_parse_grade_read_message($xml);
e27cb316 122
b9b2e7bb 123 $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
e27cb316 124
8fa50fdd
MN
125 if (!lti_accepts_grades($ltiinstance)) {
126 throw new Exception('Tool does not accept grades');
127 }
128
e3f69b58 129 // Getting the grade requires the context is set.
c288a3db 130 $context = context_course::instance($ltiinstance->course);
60bd82f6 131 $PAGE->set_context($context);
e27cb316 132
996b0fd9 133 lti_verify_sourcedid($ltiinstance, $parsed);
e27cb316 134
b9b2e7bb 135 $grade = lti_read_grade($ltiinstance, $parsed->userid);
e27cb316 136
b9b2e7bb 137 $responsexml = lti_get_response_xml(
e3f69b58 138 'success', // Empty grade is also 'success'.
b9b2e7bb
CS
139 'Result read',
140 $parsed->messageid,
141 'readResultResponse'
142 );
e27cb316 143
b9b2e7bb 144 $node = $responsexml->imsx_POXBody->readResultResponse;
ddcfda87
CS
145 $node = $node->addChild('result')->addChild('resultScore');
146 $node->addChild('language', 'en');
147 $node->addChild('textString', isset($grade) ? $grade : '');
e27cb316 148
b9b2e7bb 149 echo $responsexml->asXML();
e27cb316 150
b9b2e7bb 151 break;
e27cb316 152
b9b2e7bb
CS
153 case 'deleteResultRequest':
154 $parsed = lti_parse_grade_delete_message($xml);
e27cb316 155
b9b2e7bb 156 $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
e27cb316 157
8fa50fdd
MN
158 if (!lti_accepts_grades($ltiinstance)) {
159 throw new Exception('Tool does not accept grades');
160 }
161
996b0fd9 162 lti_verify_sourcedid($ltiinstance, $parsed);
8fa50fdd 163 lti_set_session_user($parsed->userid);
e27cb316 164
b9b2e7bb 165 $gradestatus = lti_delete_grade($ltiinstance, $parsed->userid);
e27cb316 166
00e27060
MN
167 if (!$gradestatus) {
168 throw new Exception('Grade delete request');
169 }
170
b9b2e7bb 171 $responsexml = lti_get_response_xml(
00e27060 172 'success',
e27cb316
CS
173 'Grade delete request',
174 $parsed->messageid,
b9b2e7bb
CS
175 'deleteResultResponse'
176 );
e27cb316 177
b9b2e7bb 178 echo $responsexml->asXML();
e27cb316 179
020eea1b 180 break;
e27cb316 181
020eea1b 182 default:
e3f69b58 183 // Fire an event if we get a web service request which we don't support directly.
184 // This will allow others to extend the LTI services, which I expect to be a common
185 // use case, at least until the spec matures.
d5bc7b66
MG
186 $data = new stdClass();
187 $data->body = $rawbody;
188 $data->xml = $xml;
8a8fb3a6 189 $data->messageid = lti_parse_message_id($xml);
d5bc7b66
MG
190 $data->messagetype = $messagetype;
191 $data->consumerkey = $consumerkey;
192 $data->sharedsecret = $sharedsecret;
b97d94ff
AG
193 $eventdata = array();
194 $eventdata['other'] = array();
8a8fb3a6 195 $eventdata['other']['messageid'] = $data->messageid;
b97d94ff
AG
196 $eventdata['other']['messagetype'] = $messagetype;
197 $eventdata['other']['consumerkey'] = $consumerkey;
e27cb316 198
976b5bca 199 // Before firing the event, allow subplugins a chance to handle.
8a8fb3a6 200 if (lti_extend_lti_services($data)) {
976b5bca
CS
201 break;
202 }
203
e3f69b58 204 // If an event handler handles the web service, it should set this global to true
205 // So this code knows whether to send an "operation not supported" or not.
206 global $ltiwebservicehandled;
207 $ltiwebservicehandled = false;
e27cb316 208
ba0f89e1
CS
209 try {
210 $event = \mod_lti\event\unknown_service_api_called::create($eventdata);
d5bc7b66 211 $event->set_message_data($data);
ba0f89e1
CS
212 $event->trigger();
213 } catch (Exception $e) {
e3f69b58 214 $ltiwebservicehandled = false;
ba0f89e1 215 }
e27cb316 216
e3f69b58 217 if (!$ltiwebservicehandled) {
a0ba4ec6 218 $responsexml = lti_get_response_xml(
e27cb316
CS
219 'unsupported',
220 'unsupported',
a0ba4ec6
CS
221 lti_parse_message_id($xml),
222 $messagetype
223 );
224
225 echo $responsexml->asXML();
226 }
e27cb316 227
b9b2e7bb
CS
228 break;
229}