MDL-49329 admin: Make core_plugin_manager better suited for unit testing
[moodle.git] / mod / lti / service.php
blob0075f8eba932c0ad52afb5d02b822aa646519ebd
1 <?php
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.
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/>.
17 /**
18 * LTI web service endpoints
20 * @package mod_lti
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(dirname(__FILE__) . "/../../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 moodle\mod\lti as lti;
36 $rawbody = file_get_contents("php://input");
38 if (lti_should_log_request($rawbody)) {
39 lti_log_request($rawbody);
42 foreach (lti\OAuthUtil::get_headers() as $name => $value) {
43 if ($name === 'Authorization') {
44 // TODO: Switch to core oauthlib once implemented - MDL-30149.
45 $oauthparams = lti\OAuthUtil::split_header($value);
47 $consumerkey = $oauthparams['oauth_consumer_key'];
48 break;
52 if (empty($consumerkey)) {
53 throw new Exception('Consumer key is missing.');
56 $sharedsecret = lti_verify_message($consumerkey, lti_get_shared_secrets_by_key($consumerkey), $rawbody);
58 if ($sharedsecret === false) {
59 throw new Exception('Message signature not valid');
62 // TODO MDL-46023 Replace this code with a call to the new library.
63 $origentity = libxml_disable_entity_loader(true);
64 $xml = simplexml_load_string($rawbody);
65 if (!$xml) {
66 libxml_disable_entity_loader($origentity);
67 throw new Exception('Invalid XML content');
69 libxml_disable_entity_loader($origentity);
71 $body = $xml->imsx_POXBody;
72 foreach ($body->children() as $child) {
73 $messagetype = $child->getName();
76 switch ($messagetype) {
77 case 'replaceResultRequest':
78 try {
79 $parsed = lti_parse_grade_replace_message($xml);
80 } catch (Exception $e) {
81 $responsexml = lti_get_response_xml(
82 'failure',
83 $e->getMessage(),
84 uniqid(),
85 'replaceResultResponse');
87 echo $responsexml->asXML();
88 break;
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);
102 $responsexml = lti_get_response_xml(
103 $gradestatus ? 'success' : 'failure',
104 'Grade replace response',
105 $parsed->messageid,
106 'replaceResultResponse'
109 echo $responsexml->asXML();
111 break;
113 case 'readResultRequest':
114 $parsed = lti_parse_grade_read_message($xml);
116 $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
118 if (!lti_accepts_grades($ltiinstance)) {
119 throw new Exception('Tool does not accept grades');
122 // Getting the grade requires the context is set.
123 $context = context_course::instance($ltiinstance->course);
124 $PAGE->set_context($context);
126 lti_verify_sourcedid($ltiinstance, $parsed);
128 $grade = lti_read_grade($ltiinstance, $parsed->userid);
130 $responsexml = lti_get_response_xml(
131 'success', // Empty grade is also 'success'.
132 'Result read',
133 $parsed->messageid,
134 'readResultResponse'
137 $node = $responsexml->imsx_POXBody->readResultResponse;
138 $node = $node->addChild('result')->addChild('resultScore');
139 $node->addChild('language', 'en');
140 $node->addChild('textString', isset($grade) ? $grade : '');
142 echo $responsexml->asXML();
144 break;
146 case 'deleteResultRequest':
147 $parsed = lti_parse_grade_delete_message($xml);
149 $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
151 if (!lti_accepts_grades($ltiinstance)) {
152 throw new Exception('Tool does not accept grades');
155 lti_verify_sourcedid($ltiinstance, $parsed);
156 lti_set_session_user($parsed->userid);
158 $gradestatus = lti_delete_grade($ltiinstance, $parsed->userid);
160 $responsexml = lti_get_response_xml(
161 $gradestatus ? 'success' : 'failure',
162 'Grade delete request',
163 $parsed->messageid,
164 'deleteResultResponse'
167 echo $responsexml->asXML();
169 break;
171 default:
172 // Fire an event if we get a web service request which we don't support directly.
173 // This will allow others to extend the LTI services, which I expect to be a common
174 // use case, at least until the spec matures.
175 $data = new stdClass();
176 $data->body = $rawbody;
177 $data->xml = $xml;
178 $data->messageid = lti_parse_message_id($xml);
179 $data->messagetype = $messagetype;
180 $data->consumerkey = $consumerkey;
181 $data->sharedsecret = $sharedsecret;
182 $eventdata = array();
183 $eventdata['other'] = array();
184 $eventdata['other']['messageid'] = $data->messageid;
185 $eventdata['other']['messagetype'] = $messagetype;
186 $eventdata['other']['consumerkey'] = $consumerkey;
188 // Before firing the event, allow subplugins a chance to handle.
189 if (lti_extend_lti_services($data)) {
190 break;
193 // If an event handler handles the web service, it should set this global to true
194 // So this code knows whether to send an "operation not supported" or not.
195 global $ltiwebservicehandled;
196 $ltiwebservicehandled = false;
198 try {
199 $event = \mod_lti\event\unknown_service_api_called::create($eventdata);
200 $event->set_message_data($data);
201 $event->trigger();
202 } catch (Exception $e) {
203 $ltiwebservicehandled = false;
206 if (!$ltiwebservicehandled) {
207 $responsexml = lti_get_response_xml(
208 'unsupported',
209 'unsupported',
210 lti_parse_message_id($xml),
211 $messagetype
214 echo $responsexml->asXML();
217 break;