Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
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 namespace mod_h5pactivity\xapi; 18 19 use mod_h5pactivity\local\attempt; 20 use mod_h5pactivity\local\manager; 21 use mod_h5pactivity\event\statement_received; 22 use core_xapi\local\statement; 23 use core_xapi\handler as handler_base; 24 use core\event\base as event_base; 25 use core_xapi\local\state; 26 use moodle_exception; 27 28 defined('MOODLE_INTERNAL') || die(); 29 30 global $CFG; 31 require_once($CFG->dirroot.'/mod/h5pactivity/lib.php'); 32 33 /** 34 * Class xapi_handler for H5P statements and states. 35 * 36 * @package mod_h5pactivity 37 * @since Moodle 3.9 38 * @copyright 2020 Ferran Recio <ferran@moodle.com> 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class handler extends handler_base { 42 43 /** 44 * Convert a statement object into a Moodle xAPI Event. 45 * 46 * If a statement is accepted by the xAPI webservice the component must provide 47 * an event to handle that statement, otherwise the statement will be rejected. 48 * 49 * @param statement $statement 50 * @return core\event\base|null a Moodle event to trigger 51 */ 52 public function statement_to_event(statement $statement): ?event_base { 53 54 // Only process statements with results. 55 $xapiresult = $statement->get_result(); 56 if (empty($xapiresult)) { 57 return null; 58 } 59 60 // Statements can contain any verb, for security reasons each 61 // plugin needs to filter it's own specific verbs. For now the only verbs the H5P 62 // plugin keeps track on are "answered" and "completed" because they are realted to grading. 63 // In the future this list can be increased to track more user interactions. 64 $validvalues = [ 65 'http://adlnet.gov/expapi/verbs/answered', 66 'http://adlnet.gov/expapi/verbs/completed', 67 ]; 68 $xapiverbid = $statement->get_verb_id(); 69 if (!in_array($xapiverbid, $validvalues)) { 70 return null; 71 } 72 73 // Validate object. 74 $xapiobject = $statement->get_activity_id(); 75 76 // H5P add some extra params to ID to define subcontents. 77 $parts = explode('?', $xapiobject, 2); 78 $contextid = array_shift($parts); 79 $subcontent = str_replace('subContentId=', '', array_shift($parts) ?? ''); 80 if (empty($contextid) || !is_numeric($contextid)) { 81 return null; 82 } 83 $context = \context::instance_by_id($contextid); 84 if (!$context instanceof \context_module) { 85 return null; 86 } 87 88 // As the activity does not accept group statement, the code can assume that the 89 // statement user is valid (otherwise the xAPI library will reject the statement). 90 $user = $statement->get_user(); 91 if (!has_capability('mod/h5pactivity:view', $context, $user)) { 92 return null; 93 } 94 95 $cm = get_coursemodule_from_id('h5pactivity', $context->instanceid, 0, false); 96 if (!$cm) { 97 return null; 98 } 99 100 $manager = manager::create_from_coursemodule($cm); 101 102 if (!$manager->is_tracking_enabled($user)) { 103 return null; 104 } 105 106 // For now, attempts are only processed on a single batch starting with the final "completed" 107 // and "answered" statements (this could change in the future). This initial statement have no 108 // subcontent defined as they are the main finishing statement. For this reason, this statement 109 // indicates a new attempt creation. This way, simpler H5P activies like multichoice can generate 110 // an attempt each time the user answers while complex like question-set could group all questions 111 // in a single attempt (using subcontents). 112 if (empty($subcontent)) { 113 $attempt = attempt::new_attempt($user, $cm); 114 } else { 115 $attempt = attempt::last_attempt($user, $cm); 116 } 117 if (!$attempt) { 118 return null; 119 } 120 $result = $attempt->save_statement($statement, $subcontent); 121 if (!$result) { 122 return null; 123 } 124 125 // Update activity if necessary. 126 if ($attempt->get_scoreupdated()) { 127 $grader = $manager->get_grader(); 128 $grader->update_grades($user->id); 129 } 130 131 // Convert into a Moodle event. 132 $minstatement = $statement->minify(); 133 $params = [ 134 'other' => $minstatement, 135 'context' => $context, 136 'objectid' => $cm->instance, 137 'userid' => $user->id, 138 ]; 139 return statement_received::create($params); 140 } 141 142 /** 143 * Validate a xAPI state. 144 * 145 * Check if the state is valid for this handler. 146 * 147 * This method is used also for the state get requests so the validation 148 * cannot rely on having state data. 149 * 150 * @param state $state 151 * @return bool if the state is valid or not 152 */ 153 protected function validate_state(state $state): bool { 154 $xapiobject = $state->get_activity_id(); 155 156 // H5P add some extra params to ID to define subcontents. 157 $parts = explode('?', $xapiobject, 2); 158 $contextid = array_shift($parts); 159 if (empty($contextid) || !is_numeric($contextid)) { 160 return false; 161 } 162 163 try { 164 $context = \context::instance_by_id($contextid); 165 if (!$context instanceof \context_module) { 166 return false; 167 } 168 } catch (moodle_exception $exception) { 169 return false; 170 } 171 172 $cm = get_coursemodule_from_id('h5pactivity', $context->instanceid, 0, false); 173 if (!$cm) { 174 return false; 175 } 176 177 // If tracking is not enabled, the state won't be considered valid. 178 $manager = manager::create_from_coursemodule($cm); 179 $user = $state->get_user(); 180 if (!$manager->is_tracking_enabled($user)) { 181 return false; 182 } 183 184 return true; 185 } 186 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body