Merge branch 'MDL-30328' of git://github.com/scriby/moodle
[moodle.git] / mod / lti / servicelib.php
CommitLineData
996b0fd9 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 * Utility code for LTI service handling.
61eb12d4
CS
19 *
20 * @package mod
21 * @subpackage lti
8f45215d 22 * @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
61eb12d4 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8f45215d 24 * @author Chris Scribner
61eb12d4 25 */
996b0fd9 26
a55c3619
EL
27defined('MOODLE_INTERNAL') || die;
28
996b0fd9
CS
29require_once($CFG->dirroot.'/mod/lti/OAuthBody.php');
30
fabd4fcf 31// TODO: Switch to core oauthlib once implemented - MDL-30149
795dff01
CS
32use moodle\mod\lti as lti;
33
996b0fd9
CS
34define('LTI_ITEM_TYPE', 'mod');
35define('LTI_ITEM_MODULE', 'lti');
36define('LTI_SOURCE', 'mod/lti');
37
ea04a9f9 38function lti_get_response_xml($codemajor, $description, $messageref, $messagetype) {
996b0fd9
CS
39 $xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><imsx_POXEnvelopeResponse />');
40 $xml->addAttribute('xmlns', 'http://www.imsglobal.org/lis/oms1p0/pox');
e27cb316 41
ea04a9f9 42 $headerinfo = $xml->addChild('imsx_POXHeader')->addChild('imsx_POXResponseHeaderInfo');
e27cb316 43
996b0fd9
CS
44 $headerinfo->addChild('imsx_version', 'V1.0');
45 $headerinfo->addChild('imsx_messageIdentifier', (string)mt_rand());
e27cb316 46
996b0fd9
CS
47 $statusinfo = $headerinfo->addChild('imsx_statusInfo');
48 $statusinfo->addchild('imsx_codeMajor', $codemajor);
49 $statusinfo->addChild('imsx_severity', 'status');
50 $statusinfo->addChild('imsx_description', $description);
51 $statusinfo->addChild('imsx_messageRefIdentifier', $messageref);
e27cb316 52
ea04a9f9 53 $xml->addChild('imsx_POXBody')->addChild($messagetype);
e27cb316 54
996b0fd9
CS
55 return $xml;
56}
57
ea04a9f9 58function lti_parse_message_id($xml) {
996b0fd9
CS
59 $node = $xml->imsx_POXHeader->imsx_POXRequestHeaderInfo->imsx_messageIdentifier;
60 $messageid = (string)$node;
e27cb316 61
996b0fd9
CS
62 return $messageid;
63}
64
ea04a9f9 65function lti_parse_grade_replace_message($xml) {
996b0fd9
CS
66 $node = $xml->imsx_POXBody->replaceResultRequest->resultRecord->sourcedGUID->sourcedId;
67 $resultjson = json_decode((string)$node);
e27cb316 68
996b0fd9 69 $node = $xml->imsx_POXBody->replaceResultRequest->resultRecord->result->resultScore->textString;
ddcfda87
CS
70
71 $score = (string) $node;
72 if ( ! is_numeric($score) ) {
73 throw new Exception('Score must be numeric');
74 }
75 $grade = floatval($score);
76 if ( $grade < 0.0 || $grade > 1.0 ) {
77 throw new Exception('Score not between 0.0 and 1.0');
78 }
e27cb316 79
996b0fd9
CS
80 $parsed = new stdClass();
81 $parsed->gradeval = $grade * 100;
e27cb316 82
996b0fd9
CS
83 $parsed->instanceid = $resultjson->data->instanceid;
84 $parsed->userid = $resultjson->data->userid;
f4f711d7 85 $parsed->launchid = $resultjson->data->launchid;
996b0fd9 86 $parsed->sourcedidhash = $resultjson->hash;
e27cb316 87
996b0fd9 88 $parsed->messageid = lti_parse_message_id($xml);
e27cb316 89
996b0fd9
CS
90 return $parsed;
91}
92
ea04a9f9 93function lti_parse_grade_read_message($xml) {
996b0fd9
CS
94 $node = $xml->imsx_POXBody->readResultRequest->resultRecord->sourcedGUID->sourcedId;
95 $resultjson = json_decode((string)$node);
e27cb316 96
996b0fd9
CS
97 $parsed = new stdClass();
98 $parsed->instanceid = $resultjson->data->instanceid;
99 $parsed->userid = $resultjson->data->userid;
f4f711d7 100 $parsed->launchid = $resultjson->data->launchid;
996b0fd9 101 $parsed->sourcedidhash = $resultjson->hash;
e27cb316 102
996b0fd9 103 $parsed->messageid = lti_parse_message_id($xml);
e27cb316 104
996b0fd9
CS
105 return $parsed;
106}
107
ea04a9f9 108function lti_parse_grade_delete_message($xml) {
996b0fd9
CS
109 $node = $xml->imsx_POXBody->deleteResultRequest->resultRecord->sourcedGUID->sourcedId;
110 $resultjson = json_decode((string)$node);
e27cb316 111
996b0fd9
CS
112 $parsed = new stdClass();
113 $parsed->instanceid = $resultjson->data->instanceid;
114 $parsed->userid = $resultjson->data->userid;
f4f711d7 115 $parsed->launchid = $resultjson->data->launchid;
996b0fd9 116 $parsed->sourcedidhash = $resultjson->hash;
e27cb316 117
996b0fd9 118 $parsed->messageid = lti_parse_message_id($xml);
e27cb316 119
996b0fd9
CS
120 return $parsed;
121}
122
ea04a9f9 123function lti_update_grade($ltiinstance, $userid, $launchid, $gradeval) {
f4f711d7 124 global $CFG, $DB;
996b0fd9 125 require_once($CFG->libdir . '/gradelib.php');
e27cb316 126
996b0fd9
CS
127 $params = array();
128 $params['itemname'] = $ltiinstance->name;
129
130 $grade = new stdClass();
131 $grade->userid = $userid;
132 $grade->rawgrade = $gradeval;
133
e27cb316 134 $status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade, $params);
996b0fd9 135
f4f711d7 136 $record = $DB->get_record('lti_submission', array('ltiid' => $ltiinstance->id, 'userid' => $userid, 'launchid' => $launchid), 'id');
ea04a9f9 137 if ($record) {
f4f711d7
CS
138 $id = $record->id;
139 } else {
140 $id = null;
141 }
e27cb316 142
ea04a9f9 143 if (!empty($id)) {
f4f711d7
CS
144 $DB->update_record('lti_submission', array(
145 'id' => $id,
146 'dateupdated' => time(),
147 'gradepercent' => $gradeval,
148 'state' => 2
149 ));
150 } else {
151 $DB->insert_record('lti_submission', array(
152 'ltiid' => $ltiinstance->id,
e27cb316
CS
153 'userid' => $userid,
154 'datesubmitted' => time(),
f4f711d7
CS
155 'dateupdated' => time(),
156 'gradepercent' => $gradeval,
157 'originalgrade' => $gradeval,
158 'launchid' => $launchid,
159 'state' => 1
160 ));
161 }
e27cb316 162
996b0fd9
CS
163 return $status == GRADE_UPDATE_OK;
164}
165
ea04a9f9 166function lti_read_grade($ltiinstance, $userid) {
996b0fd9
CS
167 global $CFG;
168 require_once($CFG->libdir . '/gradelib.php');
e27cb316 169
996b0fd9 170 $grades = grade_get_grades($ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, $userid);
e27cb316 171
60bd82f6 172 if (isset($grades) && isset($grades->items[0]) && is_array($grades->items[0]->grades)) {
996b0fd9
CS
173 foreach ($grades->items[0]->grades as $agrade) {
174 $grade = $agrade->grade;
ddcfda87 175 $grade = $grade / 100.0;
996b0fd9
CS
176 break;
177 }
178 }
e27cb316 179
ea04a9f9 180 if (isset($grade)) {
996b0fd9
CS
181 return $grade;
182 }
183}
184
ea04a9f9 185function lti_delete_grade($ltiinstance, $userid) {
60bd82f6
CS
186 global $CFG;
187 require_once($CFG->libdir . '/gradelib.php');
e27cb316 188
996b0fd9
CS
189 $grade = new stdClass();
190 $grade->userid = $userid;
191 $grade->rawgrade = null;
192
193 $status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade, array('deleted'=>1));
e27cb316 194
996b0fd9
CS
195 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
196}
197
ea04a9f9
EL
198function lti_verify_message($key, $sharedsecrets, $body, $headers = null) {
199 foreach ($sharedsecrets as $secret) {
020eea1b 200 $signaturefailed = false;
e27cb316 201
ea04a9f9 202 try {
fabd4fcf 203 // TODO: Switch to core oauthlib once implemented - MDL-30149
020eea1b 204 lti\handleOAuthBodyPOST($key, $secret, $body, $headers);
ea04a9f9 205 } catch (Exception $e) {
020eea1b 206 $signaturefailed = true;
996b0fd9 207 }
e27cb316 208
ea04a9f9 209 if (!$signaturefailed) {
020eea1b
CS
210 return $secret;//Return the secret used to sign the message)
211 }
996b0fd9 212 }
e27cb316 213
020eea1b 214 return false;
996b0fd9
CS
215}
216
ea04a9f9 217function lti_verify_sourcedid($ltiinstance, $parsed) {
f4f711d7 218 $sourceid = lti_build_sourcedid($parsed->instanceid, $parsed->userid, $parsed->launchid, $ltiinstance->servicesalt);
e27cb316 219
ea04a9f9 220 if ($sourceid->hash != $parsed->sourcedidhash) {
996b0fd9
CS
221 throw new Exception('SourcedId hash not valid');
222 }
223}
ddcfda87 224