Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]
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 * External grading API 19 * 20 * @package core_grading 21 * @since Moodle 2.5 22 * @copyright 2013 Paul Charsley 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 use core_external\external_api; 27 use core_external\external_format_value; 28 use core_external\external_function_parameters; 29 use core_external\external_multiple_structure; 30 use core_external\external_single_structure; 31 use core_external\external_value; 32 use core_external\external_warnings; 33 34 defined('MOODLE_INTERNAL') || die; 35 36 require_once("$CFG->dirroot/grade/grading/lib.php"); 37 38 /** 39 * core grading functions 40 */ 41 class core_grading_external extends external_api { 42 43 /** 44 * Describes the parameters for get_definitions 45 * @return external_function_parameters 46 * @since Moodle 2.5 47 */ 48 public static function get_definitions_parameters() { 49 return new external_function_parameters( 50 array( 51 'cmids' => new external_multiple_structure( 52 new external_value(PARAM_INT, 'course module id'), '1 or more course module ids'), 53 'areaname' => new external_value(PARAM_AREA, 'area name'), 54 'activeonly' => new external_value(PARAM_BOOL, 'Only the active method', VALUE_DEFAULT, 0) 55 ) 56 ); 57 } 58 59 /** 60 * Returns the definitions for the requested course module ids 61 * @param array of ints $cmids 62 * @param string $areaname 63 * @param boolean $activeonly default is false, if true, only the active method is returned 64 * @return array of areas with definitions for each requested course module id 65 * @since Moodle 2.5 66 */ 67 public static function get_definitions($cmids, $areaname, $activeonly = false) { 68 global $DB, $CFG; 69 require_once("$CFG->dirroot/grade/grading/form/lib.php"); 70 $params = self::validate_parameters(self::get_definitions_parameters(), 71 array('cmids' => $cmids, 72 'areaname' => $areaname, 73 'activeonly' => $activeonly)); 74 $warnings = array(); 75 $areas = array(); 76 foreach ($params['cmids'] as $cmid) { 77 $context = context_module::instance($cmid); 78 try { 79 self::validate_context($context); 80 } catch (Exception $e) { 81 $warnings[] = array( 82 'item' => 'module', 83 'itemid' => $cmid, 84 'message' => 'No access rights in module context', 85 'warningcode' => '1' 86 ); 87 continue; 88 } 89 // Check if the user has managegradingforms capability. 90 $isgradingmethodmanager = false; 91 if (has_capability('moodle/grade:managegradingforms', $context)) { 92 $isgradingmethodmanager = true; 93 } 94 $module = get_coursemodule_from_id('', $cmid, 0, false, MUST_EXIST); 95 $componentname = "mod_".$module->modname; 96 97 // Get the grading manager. 98 $gradingmanager = get_grading_manager($context, $componentname, $params['areaname']); 99 // Get the controller for each grading method. 100 $methods = array(); 101 if ($params['activeonly'] == true) { 102 $methods[] = $gradingmanager->get_active_method(); 103 } else { 104 $methods = array_keys($gradingmanager->get_available_methods(false)); 105 } 106 107 $area = array(); 108 $area['cmid'] = $cmid; 109 $area['contextid'] = $context->id; 110 $area['component'] = $componentname; 111 $area['areaname'] = $params['areaname']; 112 $area['activemethod'] = $gradingmanager->get_active_method(); 113 $area['definitions'] = array(); 114 115 foreach ($methods as $method) { 116 $controller = $gradingmanager->get_controller($method); 117 $def = $controller->get_definition(true); 118 if ($def == false) { 119 continue; 120 } 121 if ($isgradingmethodmanager == false) { 122 $isviewable = true; 123 if ($def->status != gradingform_controller::DEFINITION_STATUS_READY) { 124 $warnings[] = array( 125 'item' => 'module', 126 'itemid' => $cmid, 127 'message' => 'Capability moodle/grade:managegradingforms required to view draft definitions', 128 'warningcode' => '1' 129 ); 130 $isviewable = false; 131 } 132 if (!empty($def->options)) { 133 $options = json_decode($def->options); 134 if (isset($options->alwaysshowdefinition) && 135 $options->alwaysshowdefinition == 0) { 136 $warnings[] = array( 137 'item' => 'module', 138 'itemid' => $cmid, 139 'message' => 'Capability moodle/grade:managegradingforms required to preview definition', 140 'warningcode' => '1' 141 ); 142 $isviewable = false; 143 } 144 } 145 if ($isviewable == false) { 146 continue; 147 } 148 } 149 $definition = array(); 150 $definition['id'] = $def->id; 151 $definition['method'] = $method; 152 $definition['name'] = $def->name; 153 $definition['description'] = $def->description; 154 $definition['descriptionformat'] = $def->descriptionformat; 155 $definition['status'] = $def->status; 156 $definition['copiedfromid'] = $def->copiedfromid; 157 $definition['timecreated'] = $def->timecreated; 158 $definition['usercreated'] = $def->usercreated; 159 $definition['timemodified'] = $def->timemodified; 160 $definition['usermodified'] = $def->usermodified; 161 $definition['timecopied'] = $def->timecopied; 162 // Format the description text field. 163 $formattedtext = \core_external\util::format_text($definition['description'], 164 $definition['descriptionformat'], 165 $context->id, 166 $componentname, 167 'description', 168 $def->id); 169 $definition['description'] = $formattedtext[0]; 170 $definition['descriptionformat'] = $formattedtext[1]; 171 172 $details = $controller->get_external_definition_details(); 173 $items = array(); 174 foreach ($details as $key => $value) { 175 $items[$key] = self::format_text($def->{$key}, $context->id, $componentname, $def->id); 176 } 177 $definition[$method] = $items; 178 $area['definitions'][] = $definition; 179 } 180 $areas[] = $area; 181 } 182 $result = array( 183 'areas' => $areas, 184 'warnings' => $warnings 185 ); 186 return $result; 187 } 188 189 /** 190 * Recursively processes all elements in an array and runs \core_external\util::format_text()on 191 * all elements which have a text field and associated format field with a key name 192 * that ends with the text 'format'. The modified array is returned. 193 * @param array $items the array to be processed 194 * @param int $contextid 195 * @param string $componentname 196 * @param int $itemid 197 * @see \core_external\util::format_text() 198 * @return array the input array with all fields formatted 199 */ 200 private static function format_text($items, $contextid, $componentname, $itemid) { 201 $formatkeys = array(); 202 foreach ($items as $key => $value) { 203 if (!is_array($value) && substr_compare($key, 'format', -6, 6) === 0) { 204 $formatkeys[] = $key; 205 } 206 } 207 foreach ($formatkeys as $formatkey) { 208 $descriptionkey = substr($formatkey, 0, -6); 209 $formattedtext = \core_external\util::format_text($items[$descriptionkey], 210 $items[$formatkey], 211 context::instance_by_id($contextid), 212 $componentname, 213 'description', 214 $itemid); 215 $items[$descriptionkey] = $formattedtext[0]; 216 $items[$formatkey] = $formattedtext[1]; 217 } 218 foreach ($items as $key => $value) { 219 if (is_array($value)) { 220 $items[$key] = self::format_text($value, $contextid, $componentname, $itemid); 221 } 222 } 223 return $items; 224 } 225 226 /** 227 * Creates a grading area 228 * @return external_single_structure 229 * @since Moodle 2.5 230 */ 231 private static function grading_area() { 232 return new external_single_structure( 233 array ( 234 'cmid' => new external_value(PARAM_INT, 'course module id'), 235 'contextid' => new external_value(PARAM_INT, 'context id'), 236 'component' => new external_value(PARAM_TEXT, 'component name'), 237 'areaname' => new external_value(PARAM_TEXT, 'area name'), 238 'activemethod' => new external_value(PARAM_TEXT, 'active method', VALUE_OPTIONAL), 239 'definitions' => new external_multiple_structure(self::definition(), 'definitions') 240 ) 241 ); 242 } 243 244 /** 245 * creates a grading form definition 246 * @return external_single_structure 247 * @since Moodle 2.5 248 */ 249 private static function definition() { 250 global $CFG; 251 $definition = array(); 252 $definition['id'] = new external_value(PARAM_INT, 'definition id', VALUE_OPTIONAL); 253 $definition['method'] = new external_value(PARAM_TEXT, 'method'); 254 $definition['name'] = new external_value(PARAM_TEXT, 'name'); 255 $definition['description'] = new external_value(PARAM_RAW, 'description', VALUE_OPTIONAL); 256 $definition['descriptionformat'] = new external_format_value('description', VALUE_OPTIONAL); 257 $definition['status'] = new external_value(PARAM_INT, 'status'); 258 $definition['copiedfromid'] = new external_value(PARAM_INT, 'copied from id', VALUE_OPTIONAL); 259 $definition['timecreated'] = new external_value(PARAM_INT, 'creation time'); 260 $definition['usercreated'] = new external_value(PARAM_INT, 'user who created definition'); 261 $definition['timemodified'] = new external_value(PARAM_INT, 'last modified time'); 262 $definition['usermodified'] = new external_value(PARAM_INT, 'user who modified definition'); 263 $definition['timecopied'] = new external_value(PARAM_INT, 'time copied', VALUE_OPTIONAL); 264 foreach (self::get_grading_methods() as $method) { 265 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php'); 266 $details = call_user_func('gradingform_'.$method.'_controller::get_external_definition_details'); 267 if ($details != null) { 268 $items = array(); 269 foreach ($details as $key => $value) { 270 $details[$key]->required = VALUE_OPTIONAL; 271 $items[$key] = $value; 272 } 273 $definition[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL); 274 } 275 } 276 return new external_single_structure($definition); 277 } 278 279 /** 280 * Describes the get_definitions return value 281 * @return external_single_structure 282 * @since Moodle 2.5 283 */ 284 public static function get_definitions_returns() { 285 return new external_single_structure( 286 array( 287 'areas' => new external_multiple_structure(self::grading_area(), 'list of grading areas'), 288 'warnings' => new external_warnings() 289 ) 290 ); 291 } 292 293 /** 294 * @return array of available grading methods 295 * @since Moodle 2.5 296 */ 297 private static function get_grading_methods() { 298 $methods = array_keys(grading_manager::available_methods(false)); 299 return $methods; 300 } 301 302 /** 303 * Describes the parameters for get_gradingform_instances 304 * 305 * @return external_function_parameters 306 * @since Moodle 2.6 307 */ 308 public static function get_gradingform_instances_parameters() { 309 return new external_function_parameters( 310 array( 311 'definitionid' => new external_value(PARAM_INT, 'definition id'), 312 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0) 313 ) 314 ); 315 } 316 317 /** 318 * Returns the instances and fillings for the requested definition id 319 * 320 * @param int $definitionid 321 * @param int $since only return instances with timemodified >= since 322 * @return array of grading instances with fillings for the definition id 323 * @since Moodle 2.6 324 */ 325 public static function get_gradingform_instances($definitionid, $since = 0) { 326 global $DB, $CFG; 327 require_once("$CFG->dirroot/grade/grading/form/lib.php"); 328 $params = self::validate_parameters(self::get_gradingform_instances_parameters(), 329 array('definitionid' => $definitionid, 330 'since' => $since)); 331 $instances = array(); 332 $warnings = array(); 333 334 $definition = $DB->get_record('grading_definitions', 335 array('id' => $params['definitionid']), 336 'areaid,method', MUST_EXIST); 337 $area = $DB->get_record('grading_areas', 338 array('id' => $definition->areaid), 339 'contextid,component', MUST_EXIST); 340 341 $context = context::instance_by_id($area->contextid); 342 require_capability('moodle/grade:managegradingforms', $context); 343 344 $gradingmanager = get_grading_manager($definition->areaid); 345 $controller = $gradingmanager->get_controller($definition->method); 346 $activeinstances = $controller->get_all_active_instances ($params['since']); 347 $details = $controller->get_external_instance_filling_details(); 348 if ($details == null) { 349 $warnings[] = array( 350 'item' => 'definition', 351 'itemid' => $params['definitionid'], 352 'message' => 'Fillings unavailable because get_external_instance_filling_details is not defined', 353 'warningcode' => '1' 354 ); 355 } 356 $getfilling = null; 357 if (method_exists('gradingform_'.$definition->method.'_instance', 'get_'.$definition->method.'_filling')) { 358 $getfilling = 'get_'.$definition->method.'_filling'; 359 } else { 360 $warnings[] = array( 361 'item' => 'definition', 362 'itemid' => $params['definitionid'], 363 'message' => 'Fillings unavailable because get_'.$definition->method.'_filling is not defined', 364 'warningcode' => '1' 365 ); 366 } 367 foreach ($activeinstances as $activeinstance) { 368 $instance = array(); 369 $instance['id'] = $activeinstance->get_id(); 370 $instance['raterid'] = $activeinstance->get_data('raterid'); 371 $instance['itemid'] = $activeinstance->get_data('itemid'); 372 $instance['rawgrade'] = $activeinstance->get_data('rawgrade'); 373 $instance['status'] = $activeinstance->get_data('status'); 374 $instance['feedback'] = $activeinstance->get_data('feedback'); 375 $instance['feedbackformat'] = $activeinstance->get_data('feedbackformat'); 376 // Format the feedback text field. 377 $formattedtext = \core_external\util::format_text($activeinstance->get_data('feedback'), 378 $activeinstance->get_data('feedbackformat'), 379 $context->id, 380 $area->component, 381 'feedback', 382 $params['definitionid']); 383 $instance['feedback'] = $formattedtext[0]; 384 $instance['feedbackformat'] = $formattedtext[1]; 385 $instance['timemodified'] = $activeinstance->get_data('timemodified'); 386 387 if ($details != null && $getfilling != null) { 388 $fillingdata = $activeinstance->$getfilling(); 389 $filling = array(); 390 foreach ($details as $key => $value) { 391 $filling[$key] = self::format_text($fillingdata[$key], 392 $context->id, 393 $area->component, 394 $params['definitionid']); 395 } 396 $instance[$definition->method] = $filling; 397 } 398 $instances[] = $instance; 399 } 400 $result = array( 401 'instances' => $instances, 402 'warnings' => $warnings 403 ); 404 return $result; 405 } 406 407 /** 408 * Creates a grading instance 409 * 410 * @return external_single_structure 411 * @since Moodle 2.6 412 */ 413 private static function grading_instance() { 414 global $CFG; 415 $instance = array(); 416 $instance['id'] = new external_value(PARAM_INT, 'instance id'); 417 $instance['raterid'] = new external_value(PARAM_INT, 'rater id'); 418 $instance['itemid'] = new external_value(PARAM_INT, 'item id'); 419 $instance['rawgrade'] = new external_value(PARAM_TEXT, 'raw grade', VALUE_OPTIONAL); 420 $instance['status'] = new external_value(PARAM_INT, 'status'); 421 $instance['feedback'] = new external_value(PARAM_RAW, 'feedback', VALUE_OPTIONAL); 422 $instance['feedbackformat'] = new external_format_value('feedback', VALUE_OPTIONAL); 423 $instance['timemodified'] = new external_value(PARAM_INT, 'modified time'); 424 foreach (self::get_grading_methods() as $method) { 425 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php'); 426 $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details'); 427 if ($details != null) { 428 $items = array(); 429 foreach ($details as $key => $value) { 430 $details[$key]->required = VALUE_OPTIONAL; 431 $items[$key] = $value; 432 } 433 $instance[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL); 434 } 435 } 436 return new external_single_structure($instance); 437 } 438 439 /** 440 * Describes the get_gradingform_instances return value 441 * 442 * @return external_single_structure 443 * @since Moodle 2.6 444 */ 445 public static function get_gradingform_instances_returns() { 446 return new external_single_structure( 447 array( 448 'instances' => new external_multiple_structure(self::grading_instance(), 'list of grading instances'), 449 'warnings' => new external_warnings() 450 ) 451 ); 452 } 453 454 /** 455 * Describes the parameters for save_definitions 456 * 457 * @return external_function_parameters 458 * @since Moodle 2.8 459 */ 460 public static function save_definitions_parameters() { 461 return new external_function_parameters( 462 array( 463 'areas' => new external_multiple_structure(self::grading_area(), 'areas with definitions to save') 464 ) 465 ); 466 } 467 468 /** 469 * Saves the areas and definitions 470 * @param array $areas array of areas containing definitions to be saved 471 * @return null 472 * @throws invalid_parameter_exception 473 * @since Moodle 2.8 474 */ 475 public static function save_definitions($areas) { 476 $params = self::validate_parameters(self::save_definitions_parameters(), 477 array('areas' => $areas)); 478 479 foreach ($params['areas'] as $area) { 480 481 $context = context::instance_by_id($area['contextid']); 482 require_capability('moodle/grade:managegradingforms', $context); 483 $gradingmanager = get_grading_manager($context, $area['component'], $area['areaname']); 484 $gradingmanager->set_active_method($area['activemethod']); 485 $availablemethods = $gradingmanager->get_available_methods(); 486 487 foreach ($area['definitions'] as $definition) { 488 if (array_key_exists($definition['method'], $availablemethods)) { 489 $controller = $gradingmanager->get_controller($definition['method']); 490 $controller->update_definition(self::create_definition_object($definition)); 491 } else { 492 throw new invalid_parameter_exception('Unknown Grading method: '. $definition['method']); 493 } 494 } 495 } 496 } 497 498 /** 499 * Describes the return value for save_definitions 500 * 501 * @return external_single_structure 502 * @since Moodle 2.8 503 */ 504 public static function save_definitions_returns() { 505 return null; 506 } 507 508 /** 509 * Creates a definition stdClass object using the values from the definition 510 * array that is passed in as a parameter 511 * 512 * @param array $definition 513 * @return stdClass definition object 514 * @since Moodle 2.8 515 */ 516 private static function create_definition_object($definition) { 517 global $CFG; 518 519 $method = $definition['method']; 520 $definitionobject = new stdClass(); 521 foreach ($definition as $key => $value) { 522 if (!is_array($value)) { 523 $definitionobject->$key = $value; 524 } 525 } 526 $text = ''; 527 $format = FORMAT_MOODLE; 528 if (isset($definition['description'])) { 529 $text = $definition['description']; 530 if (isset($definition['descriptionformat'])) { 531 $format = $definition['descriptionformat']; 532 } 533 } 534 $definitionobject->description_editor = array('text' => $text, 'format' => $format); 535 536 require_once("$CFG->libdir/filelib.php"); 537 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php'); 538 $details = call_user_func('gradingform_'.$method.'_controller::get_external_definition_details'); 539 $methodarray = array(); 540 foreach (array_keys($details) as $definitionkey) { 541 $items = array(); 542 $idnumber = 1; 543 foreach ($definition[$method][$definitionkey] as $item) { 544 $processeditem = self::set_new_ids($item, $idnumber); 545 $items[$processeditem['id']] = $processeditem; 546 $idnumber++; 547 } 548 $definitionobjectkey = substr($definitionkey, strlen($method.'_')); 549 $methodarray[$definitionobjectkey] = $items; 550 $definitionobject->$method = $methodarray; 551 } 552 553 return $definitionobject; 554 } 555 556 /** 557 * Recursively iterates through arrays. Any array without an id key-value combination 558 * is assumed to be an array of values to be inserted and an id key-value is added with 559 * the value matching the regex '/^NEWID\d+$/' that is expected by each grading form implementation. 560 * 561 * @param array $arraytoset the array to be processed 562 * @param int $startnumber the starting number for the new id numbers 563 * @return array with missing id keys added for all arrays 564 * @since Moodle 2.8 565 */ 566 private static function set_new_ids($arraytoset, $startnumber) { 567 $result = array(); 568 $foundid = false; 569 $number = $startnumber; 570 foreach ($arraytoset as $key1 => $value1) { 571 if (is_array($value1)) { 572 foreach ($value1 as $key2 => $value2) { 573 $processedvalue = self::set_new_ids($value2, $number); 574 $result[$key1][$processedvalue['id']] = $processedvalue; 575 $number++; 576 } 577 } else { 578 $result[$key1] = $value1; 579 } 580 if ($key1 === 'id') { 581 $foundid = true; 582 } 583 } 584 if (!$foundid) { 585 $result['id'] = 'NEWID'.$number; 586 } 587 return $result; 588 } 589 590 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body