Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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.
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
/**
18
 * Utility code for LTI service handling.
19
 *
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
24
 */
25
 
26
defined('MOODLE_INTERNAL') || die;
27
 
28
require_once($CFG->dirroot.'/mod/lti/OAuthBody.php');
29
require_once($CFG->dirroot.'/mod/lti/locallib.php');
30
 
31
// TODO: Switch to core oauthlib once implemented - MDL-30149.
32
use moodle\mod\lti as lti;
33
 
34
define('LTI_ITEM_TYPE', 'mod');
35
define('LTI_ITEM_MODULE', 'lti');
36
define('LTI_SOURCE', 'mod/lti');
37
 
38
function lti_get_response_xml($codemajor, $description, $messageref, $messagetype) {
39
    $xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><imsx_POXEnvelopeResponse />');
40
    $xml->addAttribute('xmlns', 'http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0');
41
 
42
    $headerinfo = $xml->addChild('imsx_POXHeader')->addChild('imsx_POXResponseHeaderInfo');
43
 
44
    $headerinfo->addChild('imsx_version', 'V1.0');
45
    $headerinfo->addChild('imsx_messageIdentifier', (string)mt_rand());
46
 
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);
52
    $incomingtype = str_replace('Response', 'Request', $messagetype);
53
    $statusinfo->addChild('imsx_operationRefIdentifier', $incomingtype);
54
 
55
    $xml->addChild('imsx_POXBody')->addChild($messagetype);
56
 
57
    return $xml;
58
}
59
 
60
function lti_parse_message_id($xml) {
61
    if (empty($xml->imsx_POXHeader)) {
62
        return '';
63
    }
64
 
65
    $node = $xml->imsx_POXHeader->imsx_POXRequestHeaderInfo->imsx_messageIdentifier;
66
    $messageid = (string)$node;
67
 
68
    return $messageid;
69
}
70
 
71
function lti_parse_grade_replace_message($xml) {
72
    $node = $xml->imsx_POXBody->replaceResultRequest->resultRecord->sourcedGUID->sourcedId;
73
    $resultjson = json_decode((string)$node);
74
    if ( is_null($resultjson) ) {
75
        throw new Exception('Invalid sourcedId in result message');
76
    }
77
    $node = $xml->imsx_POXBody->replaceResultRequest->resultRecord->result->resultScore->textString;
78
 
79
    $score = (string) $node;
80
    if ( ! is_numeric($score) ) {
81
        throw new Exception('Score must be numeric');
82
    }
83
    $grade = floatval($score);
84
    if ( $grade < 0.0 || $grade > 1.0 ) {
85
        throw new Exception('Score not between 0.0 and 1.0');
86
    }
87
 
88
    $parsed = new stdClass();
89
    $parsed->gradeval = $grade;
90
 
91
    $parsed->instanceid = $resultjson->data->instanceid;
92
    $parsed->userid = $resultjson->data->userid;
93
    $parsed->launchid = $resultjson->data->launchid;
94
    $parsed->typeid = $resultjson->data->typeid;
95
    $parsed->sourcedidhash = $resultjson->hash;
96
 
97
    $parsed->messageid = lti_parse_message_id($xml);
98
 
99
    return $parsed;
100
}
101
 
102
function lti_parse_grade_read_message($xml) {
103
    $node = $xml->imsx_POXBody->readResultRequest->resultRecord->sourcedGUID->sourcedId;
104
    $resultjson = json_decode((string)$node);
105
    if ( is_null($resultjson) ) {
106
        throw new Exception('Invalid sourcedId in result message');
107
    }
108
 
109
    $parsed = new stdClass();
110
    $parsed->instanceid = $resultjson->data->instanceid;
111
    $parsed->userid = $resultjson->data->userid;
112
    $parsed->launchid = $resultjson->data->launchid;
113
    $parsed->typeid = $resultjson->data->typeid;
114
    $parsed->sourcedidhash = $resultjson->hash;
115
 
116
    $parsed->messageid = lti_parse_message_id($xml);
117
 
118
    return $parsed;
119
}
120
 
121
function lti_parse_grade_delete_message($xml) {
122
    $node = $xml->imsx_POXBody->deleteResultRequest->resultRecord->sourcedGUID->sourcedId;
123
    $resultjson = json_decode((string)$node);
124
    if ( is_null($resultjson) ) {
125
        throw new Exception('Invalid sourcedId in result message');
126
    }
127
 
128
    $parsed = new stdClass();
129
    $parsed->instanceid = $resultjson->data->instanceid;
130
    $parsed->userid = $resultjson->data->userid;
131
    $parsed->launchid = $resultjson->data->launchid;
132
    $parsed->typeid = $resultjson->data->typeid;
133
    $parsed->sourcedidhash = $resultjson->hash;
134
 
135
    $parsed->messageid = lti_parse_message_id($xml);
136
 
137
    return $parsed;
138
}
139
 
140
function lti_accepts_grades($ltiinstance) {
141
    global $DB;
142
 
143
    $acceptsgrades = true;
144
    $ltitype = $DB->get_record('lti_types', array('id' => $ltiinstance->typeid));
145
 
146
    if (empty($ltitype->toolproxyid)) {
147
        $typeconfig = lti_get_config($ltiinstance);
148
 
149
        $typeacceptgrades = isset($typeconfig['acceptgrades']) ? $typeconfig['acceptgrades'] : LTI_SETTING_DELEGATE;
150
 
151
        if (!($typeacceptgrades == LTI_SETTING_ALWAYS ||
152
            ($typeacceptgrades == LTI_SETTING_DELEGATE && $ltiinstance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS))) {
153
            $acceptsgrades = false;
154
        }
155
    } else {
156
        $enabledcapabilities = explode("\n", $ltitype->enabledcapability);
157
        $acceptsgrades = in_array('Result.autocreate', $enabledcapabilities) || in_array('BasicOutcome.url', $enabledcapabilities);
158
    }
159
 
160
    return $acceptsgrades;
161
}
162
 
163
/**
164
 * Set the passed user ID to the session user.
165
 *
166
 * @param int $userid
167
 */
168
function lti_set_session_user($userid) {
169
    global $DB;
170
 
171
    if ($user = $DB->get_record('user', array('id' => $userid))) {
172
        \core\session\manager::set_user($user);
173
    }
174
}
175
 
176
function lti_update_grade($ltiinstance, $userid, $launchid, $gradeval) {
177
    global $CFG, $DB;
178
    require_once($CFG->libdir . '/gradelib.php');
179
 
180
    $params = array();
181
    $params['itemname'] = $ltiinstance->name;
182
 
183
    $gradeval = $gradeval * floatval($ltiinstance->grade);
184
 
185
    $grade = new stdClass();
186
    $grade->userid   = $userid;
187
    $grade->rawgrade = $gradeval;
188
 
189
    $status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade, $params);
190
 
191
    $record = $DB->get_record('lti_submission', array('ltiid' => $ltiinstance->id, 'userid' => $userid,
192
        'launchid' => $launchid), 'id');
193
    if ($record) {
194
        $id = $record->id;
195
    } else {
196
        $id = null;
197
    }
198
 
199
    if (!empty($id)) {
200
        $DB->update_record('lti_submission', array(
201
            'id' => $id,
202
            'dateupdated' => time(),
203
            'gradepercent' => $gradeval,
204
            'state' => 2
205
        ));
206
    } else {
207
        $DB->insert_record('lti_submission', array(
208
            'ltiid' => $ltiinstance->id,
209
            'userid' => $userid,
210
            'datesubmitted' => time(),
211
            'dateupdated' => time(),
212
            'gradepercent' => $gradeval,
213
            'originalgrade' => $gradeval,
214
            'launchid' => $launchid,
215
            'state' => 1
216
        ));
217
    }
218
 
219
    return $status == GRADE_UPDATE_OK;
220
}
221
 
222
function lti_read_grade($ltiinstance, $userid) {
223
    global $CFG;
224
    require_once($CFG->libdir . '/gradelib.php');
225
 
226
    $grades = grade_get_grades($ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, $userid);
227
 
228
    $ltigrade = floatval($ltiinstance->grade);
229
 
230
    if (!empty($ltigrade) && isset($grades) && isset($grades->items[0]) && is_array($grades->items[0]->grades)) {
231
        foreach ($grades->items[0]->grades as $agrade) {
232
            $grade = $agrade->grade;
233
            if (isset($grade)) {
234
                return $grade / $ltigrade;
235
            }
236
        }
237
    }
238
}
239
 
240
function lti_delete_grade($ltiinstance, $userid) {
241
    global $CFG;
242
    require_once($CFG->libdir . '/gradelib.php');
243
 
244
    $grade = new stdClass();
245
    $grade->userid   = $userid;
246
    $grade->rawgrade = null;
247
 
248
    $status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade);
249
 
250
    return $status == GRADE_UPDATE_OK;
251
}
252
 
253
function lti_verify_message($key, $sharedsecrets, $body, $headers = null) {
254
    foreach ($sharedsecrets as $secret) {
255
        $signaturefailed = false;
256
 
257
        try {
258
            // TODO: Switch to core oauthlib once implemented - MDL-30149.
259
            lti\handle_oauth_body_post($key, $secret, $body, $headers);
260
        } catch (Exception $e) {
261
            debugging('LTI message verification failed: '.$e->getMessage());
262
            $signaturefailed = true;
263
        }
264
 
265
        if (!$signaturefailed) {
266
            return $secret; // Return the secret used to sign the message).
267
        }
268
    }
269
 
270
    return false;
271
}
272
 
273
/**
274
 * Validate source ID from external request
275
 *
276
 * @param object $ltiinstance
277
 * @param object $parsed
278
 * @throws Exception
279
 */
280
function lti_verify_sourcedid($ltiinstance, $parsed) {
281
    $sourceid = lti_build_sourcedid($parsed->instanceid, $parsed->userid,
282
        $ltiinstance->servicesalt, $parsed->typeid, $parsed->launchid);
283
 
284
    if ($sourceid->hash != $parsed->sourcedidhash) {
285
        throw new Exception('SourcedId hash not valid');
286
    }
287
}
288
 
289
/**
290
 * Extend the LTI services through the ltisource plugins
291
 *
292
 * @param stdClass $data LTI request data
293
 * @return bool
294
 * @throws coding_exception
295
 */
296
function lti_extend_lti_services($data) {
297
    $plugins = get_plugin_list_with_function('ltisource', $data->messagetype);
298
    if (!empty($plugins)) {
299
        // There can only be one.
300
        if (count($plugins) > 1) {
301
            throw new coding_exception('More than one ltisource plugin handler found');
302
        }
303
        $data->xml = new SimpleXMLElement($data->body);
304
        $callback = current($plugins);
305
        call_user_func($callback, $data);
306
 
307
        return true;
308
    }
309
    return false;
310
}