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