Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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 * Core grades external functions 19 * 20 * @package core_grades 21 * @copyright 2012 Andrew Davis 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 * @since Moodle 2.7 24 */ 25 26 defined('MOODLE_INTERNAL') || die; 27 28 require_once("$CFG->libdir/externallib.php"); 29 require_once("$CFG->libdir/gradelib.php"); 30 require_once("$CFG->dirroot/grade/edit/tree/lib.php"); 31 require_once("$CFG->dirroot/grade/querylib.php"); 32 33 /** 34 * core grades functions 35 */ 36 class core_grades_external extends external_api { 37 /** 38 * Returns description of method parameters 39 * 40 * @return external_function_parameters 41 * @since Moodle 2.7 42 * @deprecated Moodle 3.2 MDL-51373 - Please do not call this function any more. 43 * @see gradereport_user_external::get_grade_items for a similar function 44 */ 45 public static function get_grades_parameters() { 46 return new external_function_parameters( 47 array( 48 'courseid' => new external_value(PARAM_INT, 'id of course'), 49 'component' => new external_value( 50 PARAM_COMPONENT, 'A component, for example mod_forum or mod_quiz', VALUE_DEFAULT, ''), 51 'activityid' => new external_value(PARAM_INT, 'The activity ID', VALUE_DEFAULT, null), 52 'userids' => new external_multiple_structure( 53 new external_value(PARAM_INT, 'user ID'), 54 'An array of user IDs, leave empty to just retrieve grade item information', VALUE_DEFAULT, array() 55 ) 56 ) 57 ); 58 } 59 60 /** 61 * Returns student course total grade and grades for activities. 62 * This function does not return category or manual items. 63 * This function is suitable for managers or teachers not students. 64 * 65 * @param int $courseid Course id 66 * @param string $component Component name 67 * @param int $activityid Activity id 68 * @param array $userids Array of user ids 69 * @return array Array of grades 70 * @since Moodle 2.7 71 * @deprecated Moodle 3.2 MDL-51373 - Please do not call this function any more. 72 * @see gradereport_user_external::get_grade_items for a similar function 73 */ 74 public static function get_grades($courseid, $component = null, $activityid = null, $userids = array()) { 75 global $CFG, $USER, $DB; 76 77 $params = self::validate_parameters(self::get_grades_parameters(), 78 array('courseid' => $courseid, 'component' => $component, 'activityid' => $activityid, 'userids' => $userids)); 79 80 $gradesarray = array( 81 'items' => array(), 82 'outcomes' => array() 83 ); 84 85 $coursecontext = context_course::instance($params['courseid']); 86 87 try { 88 self::validate_context($coursecontext); 89 } catch (Exception $e) { 90 $exceptionparam = new stdClass(); 91 $exceptionparam->message = $e->getMessage(); 92 $exceptionparam->courseid = $params['courseid']; 93 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); 94 } 95 96 require_capability('moodle/grade:viewhidden', $coursecontext); 97 98 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST); 99 100 $access = false; 101 if (has_capability('moodle/grade:viewall', $coursecontext)) { 102 // Can view all user's grades in this course. 103 $access = true; 104 105 } else if ($course->showgrades && count($params['userids']) == 1) { 106 // Course showgrades == students/parents can access grades. 107 108 if ($params['userids'][0] == $USER->id and has_capability('moodle/grade:view', $coursecontext)) { 109 // Student can view their own grades in this course. 110 $access = true; 111 112 } else if (has_capability('moodle/grade:viewall', context_user::instance($params['userids'][0]))) { 113 // User can view the grades of this user. Parent most probably. 114 $access = true; 115 } 116 } 117 118 if (!$access) { 119 throw new moodle_exception('nopermissiontoviewgrades', 'error'); 120 } 121 122 $itemtype = null; 123 $itemmodule = null; 124 $iteminstance = null; 125 126 if (!empty($params['component'])) { 127 list($itemtype, $itemmodule) = normalize_component($params['component']); 128 } 129 130 $cm = null; 131 if (!empty($itemmodule) && !empty($params['activityid'])) { 132 if (!$cm = get_coursemodule_from_id($itemmodule, $params['activityid'])) { 133 throw new moodle_exception('invalidcoursemodule'); 134 } 135 $iteminstance = $cm->instance; 136 } 137 138 // Load all the module info. 139 $modinfo = get_fast_modinfo($params['courseid']); 140 $activityinstances = $modinfo->get_instances(); 141 142 $gradeparams = array('courseid' => $params['courseid']); 143 if (!empty($itemtype)) { 144 $gradeparams['itemtype'] = $itemtype; 145 } 146 if (!empty($itemmodule)) { 147 $gradeparams['itemmodule'] = $itemmodule; 148 } 149 if (!empty($iteminstance)) { 150 $gradeparams['iteminstance'] = $iteminstance; 151 } 152 153 if ($activitygrades = grade_item::fetch_all($gradeparams)) { 154 $canviewhidden = has_capability('moodle/grade:viewhidden', context_course::instance($params['courseid'])); 155 156 foreach ($activitygrades as $activitygrade) { 157 158 if ($activitygrade->itemtype != 'course' and $activitygrade->itemtype != 'mod') { 159 // This function currently only supports course and mod grade items. Manual and category not supported. 160 continue; 161 } 162 163 $context = $coursecontext; 164 165 if ($activitygrade->itemtype == 'course') { 166 $item = grade_get_course_grades($course->id, $params['userids']); 167 $item->itemnumber = 0; 168 169 $grades = new stdClass; 170 $grades->items = array($item); 171 $grades->outcomes = array(); 172 173 } else { 174 $cm = $activityinstances[$activitygrade->itemmodule][$activitygrade->iteminstance]; 175 $instance = $cm->instance; 176 $context = context_module::instance($cm->id, IGNORE_MISSING); 177 178 $grades = grade_get_grades($params['courseid'], $activitygrade->itemtype, 179 $activitygrade->itemmodule, $instance, $params['userids']); 180 } 181 182 // Convert from objects to arrays so all web service clients are supported. 183 // While we're doing that we also remove grades the current user can't see due to hiding. 184 foreach ($grades->items as $gradeitem) { 185 // Switch the stdClass instance for a grade item instance so we can call is_hidden() and use the ID. 186 $gradeiteminstance = self::get_grade_item( 187 $course->id, $activitygrade->itemtype, $activitygrade->itemmodule, $activitygrade->iteminstance, 0); 188 if (!$canviewhidden && $gradeiteminstance->is_hidden()) { 189 continue; 190 } 191 192 // Format mixed bool/integer parameters. 193 $gradeitem->hidden = (empty($gradeitem->hidden)) ? 0 : $gradeitem->hidden; 194 $gradeitem->locked = (empty($gradeitem->locked)) ? 0 : $gradeitem->locked; 195 196 $gradeitemarray = (array)$gradeitem; 197 $gradeitemarray['grades'] = array(); 198 199 if (!empty($gradeitem->grades)) { 200 foreach ($gradeitem->grades as $studentid => $studentgrade) { 201 if (!$canviewhidden) { 202 // Need to load the grade_grade object to check visibility. 203 $gradegradeinstance = grade_grade::fetch( 204 array( 205 'userid' => $studentid, 206 'itemid' => $gradeiteminstance->id 207 ) 208 ); 209 // The grade grade may be legitimately missing if the student has no grade. 210 if (!empty($gradegradeinstance) && $gradegradeinstance->is_hidden()) { 211 continue; 212 } 213 } 214 215 // Format mixed bool/integer parameters. 216 $studentgrade->hidden = (empty($studentgrade->hidden)) ? 0 : $studentgrade->hidden; 217 $studentgrade->locked = (empty($studentgrade->locked)) ? 0 : $studentgrade->locked; 218 $studentgrade->overridden = (empty($studentgrade->overridden)) ? 0 : $studentgrade->overridden; 219 220 if ($gradeiteminstance->itemtype != 'course' and !empty($studentgrade->feedback)) { 221 list($studentgrade->feedback, $studentgrade->feedbackformat) = 222 external_format_text($studentgrade->feedback, $studentgrade->feedbackformat, 223 $context->id, $params['component'], 'feedback', null); 224 } 225 226 $gradeitemarray['grades'][$studentid] = (array)$studentgrade; 227 // Add the student ID as some WS clients can't access the array key. 228 $gradeitemarray['grades'][$studentid]['userid'] = $studentid; 229 } 230 } 231 232 if ($gradeiteminstance->itemtype == 'course') { 233 $gradesarray['items']['course'] = $gradeitemarray; 234 $gradesarray['items']['course']['activityid'] = 'course'; 235 } else { 236 $gradesarray['items'][$cm->id] = $gradeitemarray; 237 // Add the activity ID as some WS clients can't access the array key. 238 $gradesarray['items'][$cm->id]['activityid'] = $cm->id; 239 } 240 } 241 242 foreach ($grades->outcomes as $outcome) { 243 // Format mixed bool/integer parameters. 244 $outcome->hidden = (empty($outcome->hidden)) ? 0 : $outcome->hidden; 245 $outcome->locked = (empty($outcome->locked)) ? 0 : $outcome->locked; 246 247 $gradesarray['outcomes'][$cm->id] = (array)$outcome; 248 $gradesarray['outcomes'][$cm->id]['activityid'] = $cm->id; 249 250 $gradesarray['outcomes'][$cm->id]['grades'] = array(); 251 if (!empty($outcome->grades)) { 252 foreach ($outcome->grades as $studentid => $studentgrade) { 253 if (!$canviewhidden) { 254 // Need to load the grade_grade object to check visibility. 255 $gradeiteminstance = self::get_grade_item($course->id, $activitygrade->itemtype, 256 $activitygrade->itemmodule, $activitygrade->iteminstance, 257 $activitygrade->itemnumber); 258 $gradegradeinstance = grade_grade::fetch( 259 array( 260 'userid' => $studentid, 261 'itemid' => $gradeiteminstance->id 262 ) 263 ); 264 // The grade grade may be legitimately missing if the student has no grade. 265 if (!empty($gradegradeinstance ) && $gradegradeinstance->is_hidden()) { 266 continue; 267 } 268 } 269 270 // Format mixed bool/integer parameters. 271 $studentgrade->hidden = (empty($studentgrade->hidden)) ? 0 : $studentgrade->hidden; 272 $studentgrade->locked = (empty($studentgrade->locked)) ? 0 : $studentgrade->locked; 273 274 if (!empty($studentgrade->feedback)) { 275 list($studentgrade->feedback, $studentgrade->feedbackformat) = 276 external_format_text($studentgrade->feedback, $studentgrade->feedbackformat, 277 $context->id, $params['component'], 'feedback', null); 278 } 279 280 $gradesarray['outcomes'][$cm->id]['grades'][$studentid] = (array)$studentgrade; 281 282 // Add the student ID into the grade structure as some WS clients can't access the key. 283 $gradesarray['outcomes'][$cm->id]['grades'][$studentid]['userid'] = $studentid; 284 } 285 } 286 } 287 } 288 } 289 290 return $gradesarray; 291 } 292 293 /** 294 * Get a grade item 295 * @param int $courseid Course id 296 * @param string $itemtype Item type 297 * @param string $itemmodule Item module 298 * @param int $iteminstance Item instance 299 * @param int $itemnumber Item number 300 * @return grade_item A grade_item instance 301 * @deprecated Moodle 3.2 MDL-51373 - Please do not call this function any more. 302 * @see gradereport_user_external::get_grade_items for a similar function 303 */ 304 private static function get_grade_item($courseid, $itemtype, $itemmodule = null, $iteminstance = null, $itemnumber = null) { 305 $gradeiteminstance = null; 306 if ($itemtype == 'course') { 307 $gradeiteminstance = grade_item::fetch(array('courseid' => $courseid, 'itemtype' => $itemtype)); 308 } else { 309 $gradeiteminstance = grade_item::fetch( 310 array('courseid' => $courseid, 'itemtype' => $itemtype, 311 'itemmodule' => $itemmodule, 'iteminstance' => $iteminstance, 'itemnumber' => $itemnumber)); 312 } 313 return $gradeiteminstance; 314 } 315 316 /** 317 * Returns description of method result value 318 * 319 * @return external_description 320 * @since Moodle 2.7 321 * @deprecated Moodle 3.2 MDL-51373 - Please do not call this function any more. 322 * @see gradereport_user_external::get_grade_items for a similar function 323 */ 324 public static function get_grades_returns() { 325 return new external_single_structure( 326 array( 327 'items' => new external_multiple_structure( 328 new external_single_structure( 329 array( 330 'activityid' => new external_value( 331 PARAM_ALPHANUM, 'The ID of the activity or "course" for the course grade item'), 332 'itemnumber' => new external_value(PARAM_INT, 'Will be 0 unless the module has multiple grades'), 333 'scaleid' => new external_value(PARAM_INT, 'The ID of the custom scale or 0'), 334 'name' => new external_value(PARAM_RAW, 'The module name'), 335 'grademin' => new external_value(PARAM_FLOAT, 'Minimum grade'), 336 'grademax' => new external_value(PARAM_FLOAT, 'Maximum grade'), 337 'gradepass' => new external_value(PARAM_FLOAT, 'The passing grade threshold'), 338 'locked' => new external_value(PARAM_INT, '0 means not locked, > 1 is a date to lock until'), 339 'hidden' => new external_value(PARAM_INT, '0 means not hidden, > 1 is a date to hide until'), 340 'grades' => new external_multiple_structure( 341 new external_single_structure( 342 array( 343 'userid' => new external_value( 344 PARAM_INT, 'Student ID'), 345 'grade' => new external_value( 346 PARAM_FLOAT, 'Student grade'), 347 'locked' => new external_value( 348 PARAM_INT, '0 means not locked, > 1 is a date to lock until'), 349 'hidden' => new external_value( 350 PARAM_INT, '0 means not hidden, 1 hidden, > 1 is a date to hide until'), 351 'overridden' => new external_value( 352 PARAM_INT, '0 means not overridden, > 1 means overridden'), 353 'feedback' => new external_value( 354 PARAM_RAW, 'Feedback from the grader'), 355 'feedbackformat' => new external_value( 356 PARAM_INT, 'The format of the feedback'), 357 'usermodified' => new external_value( 358 PARAM_INT, 'The ID of the last user to modify this student grade'), 359 'datesubmitted' => new external_value( 360 PARAM_INT, 'A timestamp indicating when the student submitted the activity'), 361 'dategraded' => new external_value( 362 PARAM_INT, 'A timestamp indicating when the assignment was grades'), 363 'str_grade' => new external_value( 364 PARAM_RAW, 'A string representation of the grade'), 365 'str_long_grade' => new external_value( 366 PARAM_RAW, 'A nicely formatted string representation of the grade'), 367 'str_feedback' => new external_value( 368 PARAM_RAW, 'A formatted string representation of the feedback from the grader'), 369 ) 370 ) 371 ), 372 ) 373 ) 374 ), 375 'outcomes' => new external_multiple_structure( 376 new external_single_structure( 377 array( 378 'activityid' => new external_value( 379 PARAM_ALPHANUM, 'The ID of the activity or "course" for the course grade item'), 380 'itemnumber' => new external_value(PARAM_INT, 'Will be 0 unless the module has multiple grades'), 381 'scaleid' => new external_value(PARAM_INT, 'The ID of the custom scale or 0'), 382 'name' => new external_value(PARAM_RAW, 'The module name'), 383 'locked' => new external_value(PARAM_INT, '0 means not locked, > 1 is a date to lock until'), 384 'hidden' => new external_value(PARAM_INT, '0 means not hidden, > 1 is a date to hide until'), 385 'grades' => new external_multiple_structure( 386 new external_single_structure( 387 array( 388 'userid' => new external_value( 389 PARAM_INT, 'Student ID'), 390 'grade' => new external_value( 391 PARAM_FLOAT, 'Student grade'), 392 'locked' => new external_value( 393 PARAM_INT, '0 means not locked, > 1 is a date to lock until'), 394 'hidden' => new external_value( 395 PARAM_INT, '0 means not hidden, 1 hidden, > 1 is a date to hide until'), 396 'feedback' => new external_value( 397 PARAM_RAW, 'Feedback from the grader'), 398 'feedbackformat' => new external_value( 399 PARAM_INT, 'The feedback format'), 400 'usermodified' => new external_value( 401 PARAM_INT, 'The ID of the last user to modify this student grade'), 402 'str_grade' => new external_value( 403 PARAM_RAW, 'A string representation of the grade'), 404 'str_feedback' => new external_value( 405 PARAM_RAW, 'A formatted string representation of the feedback from the grader'), 406 ) 407 ) 408 ), 409 ) 410 ), 'An array of outcomes associated with the grade items', VALUE_OPTIONAL 411 ) 412 ) 413 ); 414 415 } 416 417 /** 418 * Marking the method as deprecated. 419 * 420 * @return bool 421 */ 422 public static function get_grades_is_deprecated() { 423 return true; 424 } 425 426 /** 427 * Returns description of method parameters 428 * 429 * @return external_function_parameters 430 * @since Moodle 2.7 431 */ 432 public static function update_grades_parameters() { 433 return new external_function_parameters( 434 array( 435 'source' => new external_value(PARAM_TEXT, 'The source of the grade update'), 436 'courseid' => new external_value(PARAM_INT, 'id of course'), 437 'component' => new external_value(PARAM_COMPONENT, 'A component, for example mod_forum or mod_quiz'), 438 'activityid' => new external_value(PARAM_INT, 'The activity ID'), 439 'itemnumber' => new external_value( 440 PARAM_INT, 'grade item ID number for modules that have multiple grades. Typically this is 0.'), 441 'grades' => new external_multiple_structure( 442 new external_single_structure( 443 array( 444 'studentid' => new external_value(PARAM_INT, 'Student ID'), 445 'grade' => new external_value(PARAM_FLOAT, 'Student grade'), 446 'str_feedback' => new external_value( 447 PARAM_TEXT, 'A string representation of the feedback from the grader', VALUE_OPTIONAL), 448 ) 449 ), 'Any student grades to alter', VALUE_DEFAULT, array()), 450 'itemdetails' => new external_single_structure( 451 array( 452 'itemname' => new external_value( 453 PARAM_ALPHANUMEXT, 'The grade item name', VALUE_OPTIONAL), 454 'idnumber' => new external_value( 455 PARAM_INT, 'Arbitrary ID provided by the module responsible for the grade item', VALUE_OPTIONAL), 456 'gradetype' => new external_value( 457 PARAM_INT, 'The type of grade (0 = none, 1 = value, 2 = scale, 3 = text)', VALUE_OPTIONAL), 458 'grademax' => new external_value( 459 PARAM_FLOAT, 'Maximum grade allowed', VALUE_OPTIONAL), 460 'grademin' => new external_value( 461 PARAM_FLOAT, 'Minimum grade allowed', VALUE_OPTIONAL), 462 'scaleid' => new external_value( 463 PARAM_INT, 'The ID of the custom scale being is used', VALUE_OPTIONAL), 464 'multfactor' => new external_value( 465 PARAM_FLOAT, 'Multiply all grades by this number', VALUE_OPTIONAL), 466 'plusfactor' => new external_value( 467 PARAM_FLOAT, 'Add this to all grades', VALUE_OPTIONAL), 468 'deleted' => new external_value( 469 PARAM_BOOL, 'True if the grade item should be deleted', VALUE_OPTIONAL), 470 'hidden' => new external_value( 471 PARAM_BOOL, 'True if the grade item is hidden', VALUE_OPTIONAL), 472 ), 'Any grade item settings to alter', VALUE_DEFAULT, array() 473 ) 474 ) 475 ); 476 } 477 478 /** 479 * Update a grade item and, optionally, student grades 480 * 481 * @param string $source The source of the grade update 482 * @param int $courseid The course id 483 * @param string $component Component name 484 * @param int $activityid The activity id 485 * @param int $itemnumber The item number 486 * @param array $grades Array of grades 487 * @param array $itemdetails Array of item details 488 * @return int A status flag 489 * @since Moodle 2.7 490 */ 491 public static function update_grades($source, $courseid, $component, $activityid, 492 $itemnumber, $grades = array(), $itemdetails = array()) { 493 global $CFG; 494 495 $params = self::validate_parameters( 496 self::update_grades_parameters(), 497 array( 498 'source' => $source, 499 'courseid' => $courseid, 500 'component' => $component, 501 'activityid' => $activityid, 502 'itemnumber' => $itemnumber, 503 'grades' => $grades, 504 'itemdetails' => $itemdetails 505 ) 506 ); 507 508 list($itemtype, $itemmodule) = normalize_component($params['component']); 509 510 if (! $cm = get_coursemodule_from_id($itemmodule, $activityid)) { 511 throw new moodle_exception('invalidcoursemodule'); 512 } 513 $iteminstance = $cm->instance; 514 515 $coursecontext = context_course::instance($params['courseid']); 516 517 try { 518 self::validate_context($coursecontext); 519 } catch (Exception $e) { 520 $exceptionparam = new stdClass(); 521 $exceptionparam->message = $e->getMessage(); 522 $exceptionparam->courseid = $params['courseid']; 523 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); 524 } 525 526 $hidinggrades = false; 527 $editinggradeitem = false; 528 $editinggrades = false; 529 530 $gradestructure = array(); 531 foreach ($grades as $grade) { 532 $editinggrades = true; 533 $gradestructure[ $grade['studentid'] ] = array('userid' => $grade['studentid'], 'rawgrade' => $grade['grade']); 534 } 535 if (!empty($params['itemdetails'])) { 536 if (isset($params['itemdetails']['hidden'])) { 537 $hidinggrades = true; 538 } else { 539 $editinggradeitem = true; 540 } 541 } 542 543 if ($editinggradeitem && !has_capability('moodle/grade:manage', $coursecontext)) { 544 throw new moodle_exception('nopermissiontoviewgrades', 'error', '', null, 545 'moodle/grade:manage required to edit grade information'); 546 } 547 if ($hidinggrades && !has_capability('moodle/grade:hide', $coursecontext) && 548 !has_capability('moodle/grade:hide', $coursecontext)) { 549 throw new moodle_exception('nopermissiontoviewgrades', 'error', '', null, 550 'moodle/grade:hide required to hide grade items'); 551 } 552 if ($editinggrades && !has_capability('moodle/grade:edit', $coursecontext)) { 553 throw new moodle_exception('nopermissiontoviewgrades', 'error', '', null, 554 'moodle/grade:edit required to edit grades'); 555 } 556 557 return grade_update($params['source'], $params['courseid'], $itemtype, 558 $itemmodule, $iteminstance, $itemnumber, $gradestructure, $params['itemdetails']); 559 } 560 561 /** 562 * Returns description of method result value 563 * 564 * @return external_description 565 * @since Moodle 2.7 566 */ 567 public static function update_grades_returns() { 568 return new external_value( 569 PARAM_INT, 570 'A value like ' . GRADE_UPDATE_OK . ' => OK, ' . GRADE_UPDATE_FAILED . ' => FAILED 571 as defined in lib/grade/constants.php' 572 ); 573 } 574 575 /** 576 * Returns description of method parameters 577 * 578 * @return external_function_parameters 579 * @since Moodle 3.10 580 */ 581 public static function create_gradecategory_parameters() { 582 return new external_function_parameters( 583 [ 584 'courseid' => new external_value(PARAM_INT, 'id of course', VALUE_REQUIRED), 585 'fullname' => new external_value(PARAM_TEXT, 'fullname of category', VALUE_REQUIRED), 586 'options' => new external_single_structure([ 587 'aggregation' => new external_value(PARAM_INT, 'aggregation method', VALUE_OPTIONAL), 588 'aggregateonlygraded' => new external_value(PARAM_BOOL, 'exclude empty grades', VALUE_OPTIONAL), 589 'aggregateoutcomes' => new external_value(PARAM_BOOL, 'aggregate outcomes', VALUE_OPTIONAL), 590 'droplow' => new external_value(PARAM_INT, 'drop low grades', VALUE_OPTIONAL), 591 'itemname' => new external_value(PARAM_TEXT, 'the category total name', VALUE_OPTIONAL), 592 'iteminfo' => new external_value(PARAM_TEXT, 'the category iteminfo', VALUE_OPTIONAL), 593 'idnumber' => new external_value(PARAM_TEXT, 'the category idnumber', VALUE_OPTIONAL), 594 'gradetype' => new external_value(PARAM_INT, 'the grade type', VALUE_OPTIONAL), 595 'grademax' => new external_value(PARAM_INT, 'the grade max', VALUE_OPTIONAL), 596 'grademin' => new external_value(PARAM_INT, 'the grade min', VALUE_OPTIONAL), 597 'gradepass' => new external_value(PARAM_INT, 'the grade to pass', VALUE_OPTIONAL), 598 'display' => new external_value(PARAM_INT, 'the display type', VALUE_OPTIONAL), 599 'decimals' => new external_value(PARAM_INT, 'the decimal count', VALUE_OPTIONAL), 600 'hiddenuntil' => new external_value(PARAM_INT, 'grades hidden until', VALUE_OPTIONAL), 601 'locktime' => new external_value(PARAM_INT, 'lock grades after', VALUE_OPTIONAL), 602 'weightoverride' => new external_value(PARAM_BOOL, 'weight adjusted', VALUE_OPTIONAL), 603 'aggregationcoef2' => new external_value(PARAM_RAW, 'weight coefficient', VALUE_OPTIONAL), 604 'parentcategoryid' => new external_value(PARAM_INT, 'The parent category id', VALUE_OPTIONAL), 605 'parentcategoryidnumber' => new external_value(PARAM_TEXT, 'the parent category idnumber', VALUE_OPTIONAL), 606 ], 'optional category data', VALUE_DEFAULT, []) 607 ] 608 ); 609 } 610 611 /** 612 * Creates a gradecategory inside of the specified course. 613 * 614 * @param int $courseid the courseid to create the gradecategory in. 615 * @param string $fullname the fullname of the grade category to create. 616 * @param array $options array of options to set. 617 * 618 * @return array array of created categoryid and warnings. 619 */ 620 public static function create_gradecategory(int $courseid, string $fullname, array $options) { 621 global $CFG, $DB; 622 623 $params = self::validate_parameters(self::create_gradecategory_parameters(), 624 ['courseid' => $courseid, 'fullname' => $fullname, 'options' => $options]); 625 626 // Now params are validated, update the references. 627 $courseid = $params['courseid']; 628 $fullname = $params['fullname']; 629 $options = $params['options']; 630 631 // Check that the context and permissions are OK. 632 $context = context_course::instance($courseid); 633 self::validate_context($context); 634 require_capability('moodle/grade:manage', $context); 635 636 $defaultparentcat = new grade_category(['courseid' => $courseid, 'depth' => 1], true); 637 638 // Setup default data so WS call needs to contain only data to set. 639 // This is not done in the Parameters, so that the array of options can be optional. 640 $data = [ 641 'fullname' => $fullname, 642 'aggregation' => grade_get_setting($courseid, 'displaytype', $CFG->grade_displaytype), 643 'aggregateonlygraded' => 1, 644 'aggregateoutcomes' => 0, 645 'droplow' => 0, 646 'grade_item_itemname' => '', 647 'grade_item_iteminfo' => '', 648 'grade_item_idnumber' => '', 649 'grade_item_gradetype' => GRADE_TYPE_VALUE, 650 'grade_item_grademax' => 100, 651 'grade_item_grademin' => 1, 652 'grade_item_gradepass' => 1, 653 'grade_item_display' => GRADE_DISPLAY_TYPE_DEFAULT, 654 // Hack. This must be -2 to use the default setting. 655 'grade_item_decimals' => -2, 656 'grade_item_hiddenuntil' => 0, 657 'grade_item_locktime' => 0, 658 'grade_item_weightoverride' => 0, 659 'grade_item_aggregationcoef2' => 0, 660 'parentcategory' => $defaultparentcat->id 661 ]; 662 663 // Most of the data items need boilerplate prepended. These are the exceptions. 664 $ignorekeys = ['aggregation', 'aggregateonlygraded', 'aggregateoutcomes', 'droplow', 'parentcategoryid', 'parentcategoryidnumber']; 665 foreach ($options as $key => $value) { 666 if (!in_array($key, $ignorekeys)) { 667 $fullkey = 'grade_item_' . $key; 668 $data[$fullkey] = $value; 669 } else { 670 $data[$key] = $value; 671 } 672 } 673 674 // Handle parent category special case. 675 if (array_key_exists('parentcategoryid', $options) && $parentcat = $DB->get_record('grade_categories', 676 ['id' => $options['parentcategoryid'], 'courseid' => $courseid])) { 677 $data['parentcategory'] = $parentcat->id; 678 } else if (array_key_exists('parentcategoryidnumber', $options) && $parentcatgradeitem = $DB->get_record('grade_items', 679 ['itemtype' => 'category', 'idnumber' => $options['parentcategoryidnumber']], '*', IGNORE_MULTIPLE)) { 680 if ($parentcat = $DB->get_record('grade_categories', ['courseid' => $courseid, 'id' => $parentcatgradeitem->iteminstance])) { 681 $data['parentcategory'] = $parentcat->id; 682 } 683 } 684 685 // Create new gradecategory item. 686 $gradecategory = new grade_category(['courseid' => $courseid], false); 687 $gradecategory->apply_default_settings(); 688 $gradecategory->apply_forced_settings(); 689 690 // Data Validation. 691 if (array_key_exists('grade_item_gradetype', $data) and $data['grade_item_gradetype'] == GRADE_TYPE_SCALE) { 692 if (empty($data['grade_item_scaleid'])) { 693 $warnings[] = ['item' => 'scaleid', 'warningcode' => 'invalidscale', 694 'message' => get_string('missingscale', 'grades')]; 695 } 696 } 697 if (array_key_exists('grade_item_grademin', $data) and array_key_exists('grade_item_grademax', $data)) { 698 if (($data['grade_item_grademax'] != 0 OR $data['grade_item_grademin'] != 0) AND 699 ($data['grade_item_grademax'] == $data['grade_item_grademin'] OR 700 $data['grade_item_grademax'] < $data['grade_item_grademin'])) { 701 $warnings[] = ['item' => 'grademax', 'warningcode' => 'invalidgrade', 702 'message' => get_string('incorrectminmax', 'grades')]; 703 } 704 } 705 706 if (!empty($warnings)) { 707 return ['categoryid' => null, 'warnings' => $warnings]; 708 } 709 710 // Now call the update function with data. Transactioned so the gradebook isn't broken on bad data. 711 try { 712 $transaction = $DB->start_delegated_transaction(); 713 grade_edit_tree::update_gradecategory($gradecategory, (object) $data); 714 $transaction->allow_commit(); 715 } catch (Exception $e) { 716 // If the submitted data was broken for any reason. 717 $warnings['database'] = $e->getMessage(); 718 $transaction->rollback(); 719 return ['warnings' => $warnings]; 720 } 721 722 return['categoryid' => $gradecategory->id, 'warnings' => []]; 723 } 724 725 /** 726 * Returns description of method result value 727 * 728 * @return external_description 729 * @since Moodle 3.10 730 */ 731 public static function create_gradecategory_returns() { 732 return new external_single_structure([ 733 'categoryid' => new external_value(PARAM_INT, 'The ID of the created category', VALUE_OPTIONAL), 734 'warnings' => new external_warnings(), 735 ]); 736 } 737 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body