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 /** 18 * Web services relating to fetching of a rubric for the grading panel. 19 * 20 * @package gradingform_rubric 21 * @copyright 2019 Mathew May <mathew.solutions> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 declare(strict_types = 1); 26 27 namespace gradingform_rubric\grades\grader\gradingpanel\external; 28 29 global $CFG; 30 31 use coding_exception; 32 use context; 33 use core_grades\component_gradeitem as gradeitem; 34 use core_grades\component_gradeitems; 35 use core_external\external_api; 36 use core_external\external_function_parameters; 37 use core_external\external_multiple_structure; 38 use core_external\external_single_structure; 39 use core_external\external_value; 40 use core_external\external_warnings; 41 use stdClass; 42 use moodle_exception; 43 require_once($CFG->dirroot.'/grade/grading/form/rubric/lib.php'); 44 45 /** 46 * Web services relating to fetching of a rubric for the grading panel. 47 * 48 * @package gradingform_rubric 49 * @copyright 2019 Mathew May <mathew.solutions> 50 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 51 */ 52 class fetch extends external_api { 53 54 /** 55 * Describes the parameters for fetching the grading panel for a simple grade. 56 * 57 * @return external_function_parameters 58 * @since Moodle 3.8 59 */ 60 public static function execute_parameters(): external_function_parameters { 61 return new external_function_parameters ([ 62 'component' => new external_value( 63 PARAM_ALPHANUMEXT, 64 'The name of the component', 65 VALUE_REQUIRED 66 ), 67 'contextid' => new external_value( 68 PARAM_INT, 69 'The ID of the context being graded', 70 VALUE_REQUIRED 71 ), 72 'itemname' => new external_value( 73 PARAM_ALPHANUM, 74 'The grade item itemname being graded', 75 VALUE_REQUIRED 76 ), 77 'gradeduserid' => new external_value( 78 PARAM_INT, 79 'The ID of the user show', 80 VALUE_REQUIRED 81 ), 82 ]); 83 } 84 85 /** 86 * Fetch the data required to build a grading panel for a simple grade. 87 * 88 * @param string $component 89 * @param int $contextid 90 * @param string $itemname 91 * @param int $gradeduserid 92 * @return array 93 * @since Moodle 3.8 94 */ 95 public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid): array { 96 global $CFG, $USER; 97 require_once("{$CFG->libdir}/gradelib.php"); 98 [ 99 'component' => $component, 100 'contextid' => $contextid, 101 'itemname' => $itemname, 102 'gradeduserid' => $gradeduserid, 103 ] = self::validate_parameters(self::execute_parameters(), [ 104 'component' => $component, 105 'contextid' => $contextid, 106 'itemname' => $itemname, 107 'gradeduserid' => $gradeduserid, 108 ]); 109 110 // Validate the context. 111 $context = context::instance_by_id($contextid); 112 self::validate_context($context); 113 114 // Validate that the supplied itemname is a gradable item. 115 if (!component_gradeitems::is_valid_itemname($component, $itemname)) { 116 throw new coding_exception("The '{$itemname}' item is not valid for the '{$component}' component"); 117 } 118 119 // Fetch the gradeitem instance. 120 $gradeitem = gradeitem::instance($component, $context, $itemname); 121 122 if (RUBRIC !== $gradeitem->get_advanced_grading_method()) { 123 throw new moodle_exception( 124 "The {$itemname} item in {$component}/{$contextid} is not configured for advanced grading with a rubric" 125 ); 126 } 127 128 // Fetch the actual data. 129 $gradeduser = \core_user::get_user($gradeduserid, '*', MUST_EXIST); 130 131 // One can access its own grades. Others just if they're graders. 132 if ($gradeduserid != $USER->id) { 133 $gradeitem->require_user_can_grade($gradeduser, $USER); 134 } 135 136 return self::get_fetch_data($gradeitem, $gradeduser); 137 } 138 139 /** 140 * Get the data to be fetched and create the structure ready for Mustache. 141 * 142 * @param gradeitem $gradeitem 143 * @param stdClass $gradeduser 144 * @return array 145 */ 146 public static function get_fetch_data(gradeitem $gradeitem, stdClass $gradeduser): array { 147 global $USER; 148 // Set up all the controllers etc that we'll be needing. 149 $hasgrade = $gradeitem->user_has_grade($gradeduser); 150 $grade = $gradeitem->get_formatted_grade_for_user($gradeduser, $USER); 151 $instance = $gradeitem->get_advanced_grading_instance($USER, $grade); 152 if (!$instance) { 153 throw new moodle_exception('error:gradingunavailable', 'grading'); 154 } 155 156 $controller = $instance->get_controller(); 157 $definition = $controller->get_definition(); 158 $fillings = $instance->get_rubric_filling(); 159 $context = $controller->get_context(); 160 $definitionid = (int) $definition->id; 161 162 // Set up some items we need to return on other interfaces. 163 $gradegrade = \grade_grade::fetch(['itemid' => $gradeitem->get_grade_item()->id, 'userid' => $gradeduser->id]); 164 $gradername = $gradegrade ? fullname(\core_user::get_user($gradegrade->usermodified)) : null; 165 $maxgrade = max(array_keys($controller->get_grade_range())); 166 167 $teacherdescription = self::get_formatted_text( 168 $context, 169 $definitionid, 170 'description', 171 $definition->description, 172 (int) $definition->descriptionformat 173 ); 174 175 $criterion = []; 176 if ($definition->rubric_criteria) { 177 // Iterate over the defined criterion in the rubric and map out what we need to render each item. 178 $criterion = array_map(function($criterion) use ($definitionid, $fillings, $context, $hasgrade) { 179 // The general structure we'll be returning, we still need to get the remark (if any) and the levels associated. 180 $result = [ 181 'id' => $criterion['id'], 182 'description' => self::get_formatted_text( 183 $context, 184 $definitionid, 185 'description', 186 $criterion['description'], 187 (int) $criterion['descriptionformat'] 188 ), 189 ]; 190 191 // Do we have an existing grade filling? if so lets get the remark associated to this criteria. 192 $filling = []; 193 if (array_key_exists($criterion['id'], $fillings['criteria'])) { 194 $filling = $fillings['criteria'][$criterion['id']]; 195 $result['remark'] = self::get_formatted_text($context, 196 $definitionid, 197 'remark', 198 $filling['remark'], 199 (int) $filling['remarkformat'] 200 ); 201 } 202 203 // Lets build the levels within a criteria and figure out what needs to go where. 204 $result['levels'] = array_map(function($level) use ($criterion, $filling, $context, $definitionid) { 205 // The bulk of what'll be returned can be defined easily we'll add to this further down. 206 $result = [ 207 'id' => $level['id'], 208 'criterionid' => $criterion['id'], 209 'score' => $level['score'], 210 'definition' => self::get_formatted_text( 211 $context, 212 $definitionid, 213 'definition', 214 $level['definition'], 215 (int) $level['definitionformat'] 216 ), 217 'checked' => null, 218 ]; 219 220 // Consult the grade filling to see if a level has been selected and if it is the current level. 221 if (array_key_exists('levelid', $filling) && $filling['levelid'] == $level['id']) { 222 $result['checked'] = true; 223 } 224 225 return $result; 226 }, $criterion['levels']); 227 228 $nulllevel = [ 229 'id' => null, 230 'criterionid' => $criterion['id'], 231 'score' => '-', 232 'definition' => get_string('notset', 'gradingform_rubric'), 233 'checked' => !$hasgrade, 234 ]; 235 // Consult the grade filling to see if a level has been selected and if it is the current level. 236 if (array_key_exists('levelid', $filling) && $filling['levelid'] == 0) { 237 $nulllevel['checked'] = true; 238 } 239 240 array_unshift($result['levels'], $nulllevel); 241 242 return $result; 243 }, $definition->rubric_criteria); 244 } 245 246 return [ 247 'templatename' => 'gradingform_rubric/grades/grader/gradingpanel', 248 'hasgrade' => $hasgrade, 249 'grade' => [ 250 'instanceid' => $instance->get_id(), 251 'criteria' => $criterion, 252 'rubricmode' => 'evaluate editable', 253 'teacherdescription' => $teacherdescription, 254 'canedit' => false, 255 'usergrade' => $grade->usergrade, 256 'maxgrade' => $maxgrade, 257 'gradedby' => $gradername, 258 'timecreated' => $grade->timecreated, 259 'timemodified' => $grade->timemodified, 260 ], 261 'warnings' => [], 262 ]; 263 } 264 265 /** 266 * Describes the data returned from the external function. 267 * 268 * @return external_single_structure 269 * @since Moodle 3.8 270 */ 271 public static function execute_returns(): external_single_structure { 272 return new external_single_structure([ 273 'templatename' => new external_value(PARAM_SAFEPATH, 'The template to use when rendering this data'), 274 'hasgrade' => new external_value(PARAM_BOOL, 'Does the user have a grade?'), 275 'grade' => new external_single_structure([ 276 'instanceid' => new external_value(PARAM_INT, 'The id of the current grading instance'), 277 'rubricmode' => new external_value(PARAM_RAW, 'The mode i.e. evaluate editable'), 278 'canedit' => new external_value(PARAM_BOOL, 'Can the user edit this'), 279 'criteria' => new external_multiple_structure( 280 new external_single_structure([ 281 'id' => new external_value(PARAM_INT, 'ID of the Criteria'), 282 'description' => new external_value(PARAM_RAW, 'Description of the Criteria'), 283 'remark' => new external_value(PARAM_RAW, 'Any remarks for this criterion for the user being assessed', VALUE_OPTIONAL), 284 'levels' => new external_multiple_structure(new external_single_structure([ 285 'id' => new external_value(PARAM_INT, 'ID of level'), 286 'criterionid' => new external_value(PARAM_INT, 'ID of the criterion this matches to'), 287 'score' => new external_value(PARAM_RAW, 'What this level is worth'), 288 'definition' => new external_value(PARAM_RAW, 'Definition of the level'), 289 'checked' => new external_value(PARAM_BOOL, 'Selected flag'), 290 ])), 291 ]) 292 ), 293 'timecreated' => new external_value(PARAM_INT, 'The time that the grade was created'), 294 'usergrade' => new external_value(PARAM_RAW, 'Current user grade'), 295 'maxgrade' => new external_value(PARAM_RAW, 'Max possible grade'), 296 'gradedby' => new external_value(PARAM_RAW, 'The assumed grader of this grading instance'), 297 'timemodified' => new external_value(PARAM_INT, 'The time that the grade was last updated'), 298 ]), 299 'warnings' => new external_warnings(), 300 ]); 301 } 302 303 /** 304 * Get a formatted version of the remark/description/etc. 305 * 306 * @param context $context 307 * @param int $definitionid 308 * @param string $filearea The file area of the field 309 * @param string $text The text to be formatted 310 * @param int $format The input format of the string 311 * @return string 312 */ 313 protected static function get_formatted_text(context $context, int $definitionid, string $filearea, string $text, int $format): string { 314 $formatoptions = [ 315 'noclean' => false, 316 'trusted' => false, 317 'filter' => true, 318 ]; 319 [$newtext] = \core_external\util::format_text( 320 $text, 321 $format, 322 $context, 323 'grading', 324 $filearea, 325 $definitionid, 326 $formatoptions 327 ); 328 return $newtext; 329 } 330 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body