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 * Choice module external API 19 * 20 * @package mod_choice 21 * @category external 22 * @copyright 2015 Costantino Cito <ccito@cvaconsulting.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 * @since Moodle 3.0 25 */ 26 27 use core_course\external\helper_for_get_mods_by_courses; 28 use core_external\external_api; 29 use core_external\external_function_parameters; 30 use core_external\external_multiple_structure; 31 use core_external\external_single_structure; 32 use core_external\external_value; 33 use core_external\external_warnings; 34 use core_external\util; 35 36 defined('MOODLE_INTERNAL') || die; 37 require_once($CFG->dirroot . '/mod/choice/lib.php'); 38 39 /** 40 * Choice module external functions 41 * 42 * @package mod_choice 43 * @category external 44 * @copyright 2015 Costantino Cito <ccito@cvaconsulting.com> 45 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 46 * @since Moodle 3.0 47 */ 48 class mod_choice_external extends external_api { 49 50 /** 51 * Describes the parameters for get_choices_by_courses. 52 * 53 * @return external_function_parameters 54 * @since Moodle 3.0 55 */ 56 public static function get_choice_results_parameters() { 57 return new external_function_parameters (array('choiceid' => new external_value(PARAM_INT, 'choice instance id'))); 58 } 59 /** 60 * Returns user's results for a specific choice 61 * and a list of those users that did not answered yet. 62 * 63 * @param int $choiceid the choice instance id 64 * @return array of responses details 65 * @since Moodle 3.0 66 */ 67 public static function get_choice_results($choiceid) { 68 global $USER, $PAGE; 69 70 $params = self::validate_parameters(self::get_choice_results_parameters(), array('choiceid' => $choiceid)); 71 72 if (!$choice = choice_get_choice($params['choiceid'])) { 73 throw new moodle_exception("invalidcoursemodule", "error"); 74 } 75 list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice'); 76 77 $context = context_module::instance($cm->id); 78 self::validate_context($context); 79 80 $groupmode = groups_get_activity_groupmode($cm); 81 // Check if we have to include responses from inactive users. 82 $onlyactive = $choice->includeinactive ? false : true; 83 $users = choice_get_response_data($choice, $cm, $groupmode, $onlyactive); 84 // Show those who haven't answered the question. 85 if (!empty($choice->showunanswered)) { 86 $choice->option[0] = get_string('notanswered', 'choice'); 87 $choice->maxanswers[0] = 0; 88 } 89 $results = prepare_choice_show_results($choice, $course, $cm, $users); 90 91 $options = array(); 92 $fullnamecap = has_capability('moodle/site:viewfullnames', $context); 93 foreach ($results->options as $optionid => $option) { 94 95 $userresponses = array(); 96 $numberofuser = 0; 97 $percentageamount = 0; 98 if (property_exists($option, 'user') and 99 (has_capability('mod/choice:readresponses', $context) or choice_can_view_results($choice))) { 100 $numberofuser = count($option->user); 101 $percentageamount = ((float)$numberofuser / (float)$results->numberofuser) * 100.0; 102 if ($choice->publish) { 103 foreach ($option->user as $userresponse) { 104 $response = array(); 105 $response['userid'] = $userresponse->id; 106 $response['fullname'] = fullname($userresponse, $fullnamecap); 107 108 $userpicture = new user_picture($userresponse); 109 $userpicture->size = 1; // Size f1. 110 $response['profileimageurl'] = $userpicture->get_url($PAGE)->out(false); 111 112 // Add optional properties. 113 foreach (array('answerid', 'timemodified') as $field) { 114 if (property_exists($userresponse, 'answerid')) { 115 $response[$field] = $userresponse->$field; 116 } 117 } 118 $userresponses[] = $response; 119 } 120 } 121 } 122 123 $options[] = array('id' => $optionid, 124 'text' => \core_external\util::format_string($option->text, $context->id), 125 'maxanswer' => $option->maxanswer, 126 'userresponses' => $userresponses, 127 'numberofuser' => $numberofuser, 128 'percentageamount' => $percentageamount 129 ); 130 } 131 132 $warnings = array(); 133 return array( 134 'options' => $options, 135 'warnings' => $warnings 136 ); 137 } 138 139 /** 140 * Describes the get_choice_results return value. 141 * 142 * @return external_single_structure 143 * @since Moodle 3.0 144 */ 145 public static function get_choice_results_returns() { 146 return new external_single_structure( 147 array( 148 'options' => new external_multiple_structure( 149 new external_single_structure( 150 array( 151 'id' => new external_value(PARAM_INT, 'choice instance id'), 152 'text' => new external_value(PARAM_RAW, 'text of the choice'), 153 'maxanswer' => new external_value(PARAM_INT, 'maximum number of answers'), 154 'userresponses' => new external_multiple_structure( 155 new external_single_structure( 156 array( 157 'userid' => new external_value(PARAM_INT, 'user id'), 158 'fullname' => new external_value(PARAM_NOTAGS, 'user full name'), 159 'profileimageurl' => new external_value(PARAM_URL, 'profile user image url'), 160 'answerid' => new external_value(PARAM_INT, 'answer id', VALUE_OPTIONAL), 161 'timemodified' => new external_value(PARAM_INT, 'time of modification', VALUE_OPTIONAL), 162 ), 'User responses' 163 ) 164 ), 165 'numberofuser' => new external_value(PARAM_INT, 'number of users answers'), 166 'percentageamount' => new external_value(PARAM_FLOAT, 'percentage of users answers') 167 ), 'Options' 168 ) 169 ), 170 'warnings' => new external_warnings(), 171 ) 172 ); 173 } 174 175 /** 176 * Describes the parameters for mod_choice_get_choice_options. 177 * 178 * @return external_function_parameters 179 * @since Moodle 3.0 180 */ 181 public static function get_choice_options_parameters() { 182 return new external_function_parameters (array('choiceid' => new external_value(PARAM_INT, 'choice instance id'))); 183 } 184 185 /** 186 * Returns options for a specific choice 187 * 188 * @param int $choiceid the choice instance id 189 * @return array of options details 190 * @since Moodle 3.0 191 */ 192 public static function get_choice_options($choiceid) { 193 global $USER; 194 $warnings = array(); 195 $params = self::validate_parameters(self::get_choice_options_parameters(), array('choiceid' => $choiceid)); 196 197 if (!$choice = choice_get_choice($params['choiceid'])) { 198 throw new moodle_exception("invalidcoursemodule", "error"); 199 } 200 list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice'); 201 202 $context = context_module::instance($cm->id); 203 self::validate_context($context); 204 205 require_capability('mod/choice:choose', $context); 206 207 $groupmode = groups_get_activity_groupmode($cm); 208 $onlyactive = $choice->includeinactive ? false : true; 209 $allresponses = choice_get_response_data($choice, $cm, $groupmode, $onlyactive); 210 211 $timenow = time(); 212 $choiceopen = true; 213 $showpreview = false; 214 215 if (!empty($choice->timeopen) && ($choice->timeopen > $timenow)) { 216 $choiceopen = false; 217 $warnings[1] = get_string("notopenyet", "choice", userdate($choice->timeopen)); 218 if ($choice->showpreview) { 219 $warnings[2] = get_string('previewonly', 'choice', userdate($choice->timeopen)); 220 $showpreview = true; 221 } 222 } 223 if (!empty($choice->timeclose) && ($timenow > $choice->timeclose)) { 224 $choiceopen = false; 225 $warnings[3] = get_string("expired", "choice", userdate($choice->timeclose)); 226 } 227 228 $optionsarray = array(); 229 230 if ($choiceopen or $showpreview) { 231 232 $options = choice_prepare_options($choice, $USER, $cm, $allresponses); 233 234 foreach ($options['options'] as $option) { 235 $optionarr = array(); 236 $optionarr['id'] = $option->attributes->value; 237 $optionarr['text'] = \core_external\util::format_string($option->text, $context->id); 238 $optionarr['maxanswers'] = $option->maxanswers; 239 $optionarr['displaylayout'] = $option->displaylayout; 240 $optionarr['countanswers'] = $option->countanswers; 241 foreach (array('checked', 'disabled') as $field) { 242 if (property_exists($option->attributes, $field) and $option->attributes->$field == 1) { 243 $optionarr[$field] = 1; 244 } else { 245 $optionarr[$field] = 0; 246 } 247 } 248 // When showpreview is active, we show options as disabled. 249 if ($showpreview or ($optionarr['checked'] == 1 and !$choice->allowupdate)) { 250 $optionarr['disabled'] = 1; 251 } 252 $optionsarray[] = $optionarr; 253 } 254 } 255 foreach ($warnings as $key => $message) { 256 $warnings[$key] = array( 257 'item' => 'choice', 258 'itemid' => $cm->id, 259 'warningcode' => $key, 260 'message' => $message 261 ); 262 } 263 return array( 264 'options' => $optionsarray, 265 'warnings' => $warnings 266 ); 267 } 268 269 /** 270 * Describes the get_choice_results return value. 271 * 272 * @return external_multiple_structure 273 * @since Moodle 3.0 274 */ 275 public static function get_choice_options_returns() { 276 return new external_single_structure( 277 array( 278 'options' => new external_multiple_structure( 279 new external_single_structure( 280 array( 281 'id' => new external_value(PARAM_INT, 'option id'), 282 'text' => new external_value(PARAM_RAW, 'text of the choice'), 283 'maxanswers' => new external_value(PARAM_INT, 'maximum number of answers'), 284 'displaylayout' => new external_value(PARAM_BOOL, 'true for orizontal, otherwise vertical'), 285 'countanswers' => new external_value(PARAM_INT, 'number of answers'), 286 'checked' => new external_value(PARAM_BOOL, 'we already answered'), 287 'disabled' => new external_value(PARAM_BOOL, 'option disabled'), 288 ) 289 ), 'Options' 290 ), 291 'warnings' => new external_warnings(), 292 ) 293 ); 294 } 295 296 /** 297 * Describes the parameters for submit_choice_response. 298 * 299 * @return external_function_parameters 300 * @since Moodle 3.0 301 */ 302 public static function submit_choice_response_parameters() { 303 return new external_function_parameters ( 304 array( 305 'choiceid' => new external_value(PARAM_INT, 'choice instance id'), 306 'responses' => new external_multiple_structure( 307 new external_value(PARAM_INT, 'answer id'), 308 'Array of response ids' 309 ), 310 ) 311 ); 312 } 313 314 /** 315 * Submit choice responses 316 * 317 * @param int $choiceid the choice instance id 318 * @param array $responses the response ids 319 * @return array answers information and warnings 320 * @since Moodle 3.0 321 */ 322 public static function submit_choice_response($choiceid, $responses) { 323 global $USER; 324 325 $warnings = array(); 326 $params = self::validate_parameters(self::submit_choice_response_parameters(), 327 array( 328 'choiceid' => $choiceid, 329 'responses' => $responses 330 )); 331 332 if (!$choice = choice_get_choice($params['choiceid'])) { 333 throw new moodle_exception("invalidcoursemodule", "error"); 334 } 335 list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice'); 336 337 $context = context_module::instance($cm->id); 338 self::validate_context($context); 339 340 require_capability('mod/choice:choose', $context); 341 342 $timenow = time(); 343 if (!empty($choice->timeopen) && ($choice->timeopen > $timenow)) { 344 throw new moodle_exception("notopenyet", "choice", '', userdate($choice->timeopen)); 345 } else if (!empty($choice->timeclose) && ($timenow > $choice->timeclose)) { 346 throw new moodle_exception("expired", "choice", '', userdate($choice->timeclose)); 347 } 348 349 if (!choice_get_my_response($choice) or $choice->allowupdate) { 350 // When a single response is given, we convert the array to a simple variable 351 // in order to avoid choice_user_submit_response to check with allowmultiple even 352 // for a single response. 353 if (count($params['responses']) == 1) { 354 $params['responses'] = reset($params['responses']); 355 } 356 choice_user_submit_response($params['responses'], $choice, $USER->id, $course, $cm); 357 } else { 358 throw new moodle_exception('missingrequiredcapability', 'webservice', '', 'allowupdate'); 359 } 360 $answers = choice_get_my_response($choice); 361 362 return array( 363 'answers' => $answers, 364 'warnings' => $warnings 365 ); 366 } 367 368 /** 369 * Describes the submit_choice_response return value. 370 * 371 * @return external_multiple_structure 372 * @since Moodle 3.0 373 */ 374 public static function submit_choice_response_returns() { 375 return new external_single_structure( 376 array( 377 'answers' => new external_multiple_structure( 378 new external_single_structure( 379 array( 380 'id' => new external_value(PARAM_INT, 'answer id'), 381 'choiceid' => new external_value(PARAM_INT, 'choiceid'), 382 'userid' => new external_value(PARAM_INT, 'user id'), 383 'optionid' => new external_value(PARAM_INT, 'optionid'), 384 'timemodified' => new external_value(PARAM_INT, 'time of last modification') 385 ), 'Answers' 386 ) 387 ), 388 'warnings' => new external_warnings(), 389 ) 390 ); 391 } 392 393 /** 394 * Returns description of method parameters 395 * 396 * @return external_function_parameters 397 * @since Moodle 3.0 398 */ 399 public static function view_choice_parameters() { 400 return new external_function_parameters( 401 array( 402 'choiceid' => new external_value(PARAM_INT, 'choice instance id') 403 ) 404 ); 405 } 406 407 /** 408 * Trigger the course module viewed event and update the module completion status. 409 * 410 * @param int $choiceid the choice instance id 411 * @return array of warnings and status result 412 * @since Moodle 3.0 413 * @throws moodle_exception 414 */ 415 public static function view_choice($choiceid) { 416 global $CFG; 417 418 $params = self::validate_parameters(self::view_choice_parameters(), 419 array( 420 'choiceid' => $choiceid 421 )); 422 $warnings = array(); 423 424 // Request and permission validation. 425 if (!$choice = choice_get_choice($params['choiceid'])) { 426 throw new moodle_exception("invalidcoursemodule", "error"); 427 } 428 list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice'); 429 430 $context = context_module::instance($cm->id); 431 self::validate_context($context); 432 433 // Trigger course_module_viewed event and completion. 434 choice_view($choice, $course, $cm, $context); 435 436 $result = array(); 437 $result['status'] = true; 438 $result['warnings'] = $warnings; 439 return $result; 440 } 441 442 /** 443 * Returns description of method result value 444 * 445 * @return \core_external\external_description 446 * @since Moodle 3.0 447 */ 448 public static function view_choice_returns() { 449 return new external_single_structure( 450 array( 451 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 452 'warnings' => new external_warnings() 453 ) 454 ); 455 } 456 457 /** 458 * Describes the parameters for get_choices_by_courses. 459 * 460 * @return external_function_parameters 461 * @since Moodle 3.0 462 */ 463 public static function get_choices_by_courses_parameters() { 464 return new external_function_parameters ( 465 array( 466 'courseids' => new external_multiple_structure( 467 new external_value(PARAM_INT, 'course id'), 'Array of course ids', VALUE_DEFAULT, array() 468 ), 469 ) 470 ); 471 } 472 473 /** 474 * Returns a list of choices in a provided list of courses, 475 * if no list is provided all choices that the user can view will be returned. 476 * 477 * @param array $courseids the course ids 478 * @return array of choices details 479 * @since Moodle 3.0 480 */ 481 public static function get_choices_by_courses($courseids = array()) { 482 $returnedchoices = array(); 483 $warnings = array(); 484 485 $params = self::validate_parameters(self::get_choices_by_courses_parameters(), array('courseids' => $courseids)); 486 487 $courses = array(); 488 if (empty($params['courseids'])) { 489 $courses = enrol_get_my_courses(); 490 $params['courseids'] = array_keys($courses); 491 } 492 493 // Ensure there are courseids to loop through. 494 if (!empty($params['courseids'])) { 495 496 list($courses, $warnings) = util::validate_courses($params['courseids'], $courses); 497 498 // Get the choices in this course, this function checks users visibility permissions. 499 // We can avoid then additional validate_context calls. 500 $choices = get_all_instances_in_courses("choice", $courses); 501 foreach ($choices as $choice) { 502 $context = context_module::instance($choice->coursemodule); 503 504 $choicedetails = helper_for_get_mods_by_courses::standard_coursemodule_element_values($choice, 'mod_choice'); 505 506 if (has_capability('mod/choice:choose', $context)) { 507 $choicedetails['publish'] = $choice->publish; 508 $choicedetails['showresults'] = $choice->showresults; 509 $choicedetails['showpreview'] = $choice->showpreview; 510 $choicedetails['timeopen'] = $choice->timeopen; 511 $choicedetails['timeclose'] = $choice->timeclose; 512 $choicedetails['display'] = $choice->display; 513 $choicedetails['allowupdate'] = $choice->allowupdate; 514 $choicedetails['allowmultiple'] = $choice->allowmultiple; 515 $choicedetails['limitanswers'] = $choice->limitanswers; 516 $choicedetails['showunanswered'] = $choice->showunanswered; 517 $choicedetails['includeinactive'] = $choice->includeinactive; 518 $choicedetails['showavailable'] = $choice->showavailable; 519 } 520 521 if (has_capability('moodle/course:manageactivities', $context)) { 522 $choicedetails['timemodified'] = $choice->timemodified; 523 $choicedetails['completionsubmit'] = $choice->completionsubmit; 524 } 525 $returnedchoices[] = $choicedetails; 526 } 527 } 528 $result = array(); 529 $result['choices'] = $returnedchoices; 530 $result['warnings'] = $warnings; 531 return $result; 532 } 533 534 /** 535 * Describes the mod_choice_get_choices_by_courses return value. 536 * 537 * @return external_single_structure 538 * @since Moodle 3.0 539 */ 540 public static function get_choices_by_courses_returns() { 541 return new external_single_structure( 542 array( 543 'choices' => new external_multiple_structure( 544 new external_single_structure(array_merge( 545 helper_for_get_mods_by_courses::standard_coursemodule_elements_returns(), 546 [ 547 'publish' => new external_value(PARAM_BOOL, 'If choice is published', VALUE_OPTIONAL), 548 'showresults' => new external_value(PARAM_INT, '0 never, 1 after answer, 2 after close, 3 always', 549 VALUE_OPTIONAL), 550 'display' => new external_value(PARAM_INT, 'Display mode (vertical, horizontal)', VALUE_OPTIONAL), 551 'allowupdate' => new external_value(PARAM_BOOL, 'Allow update', VALUE_OPTIONAL), 552 'allowmultiple' => new external_value(PARAM_BOOL, 'Allow multiple choices', VALUE_OPTIONAL), 553 'showunanswered' => new external_value(PARAM_BOOL, 'Show users who not answered yet', VALUE_OPTIONAL), 554 'includeinactive' => new external_value(PARAM_BOOL, 'Include inactive users', VALUE_OPTIONAL), 555 'limitanswers' => new external_value(PARAM_BOOL, 'Limit unswers', VALUE_OPTIONAL), 556 'timeopen' => new external_value(PARAM_INT, 'Date of opening validity', VALUE_OPTIONAL), 557 'timeclose' => new external_value(PARAM_INT, 'Date of closing validity', VALUE_OPTIONAL), 558 'showpreview' => new external_value(PARAM_BOOL, 'Show preview before timeopen', VALUE_OPTIONAL), 559 'timemodified' => new external_value(PARAM_INT, 'Time of last modification', VALUE_OPTIONAL), 560 'completionsubmit' => new external_value(PARAM_BOOL, 'Completion on user submission', VALUE_OPTIONAL), 561 'showavailable' => new external_value(PARAM_BOOL, 'Show available spaces', VALUE_OPTIONAL), 562 ] 563 ), 'Choices') 564 ), 565 'warnings' => new external_warnings(), 566 ) 567 ); 568 } 569 570 /** 571 * Describes the parameters for delete_choice_responses. 572 * 573 * @return external_function_parameters 574 * @since Moodle 3.0 575 */ 576 public static function delete_choice_responses_parameters() { 577 return new external_function_parameters ( 578 array( 579 'choiceid' => new external_value(PARAM_INT, 'choice instance id'), 580 'responses' => new external_multiple_structure( 581 new external_value(PARAM_INT, 'response id'), 582 'Array of response ids, empty for deleting all the current user responses.', 583 VALUE_DEFAULT, 584 array() 585 ), 586 ) 587 ); 588 } 589 590 /** 591 * Delete the given submitted responses in a choice 592 * 593 * @param int $choiceid the choice instance id 594 * @param array $responses the response ids, empty for deleting all the current user responses 595 * @return array status information and warnings 596 * @throws moodle_exception 597 * @since Moodle 3.0 598 */ 599 public static function delete_choice_responses($choiceid, $responses = array()) { 600 601 $status = false; 602 $warnings = array(); 603 $params = self::validate_parameters(self::delete_choice_responses_parameters(), 604 array( 605 'choiceid' => $choiceid, 606 'responses' => $responses 607 )); 608 609 if (!$choice = choice_get_choice($params['choiceid'])) { 610 throw new moodle_exception("invalidcoursemodule", "error"); 611 } 612 list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice'); 613 614 $context = context_module::instance($cm->id); 615 self::validate_context($context); 616 617 require_capability('mod/choice:choose', $context); 618 619 $candeleteall = has_capability('mod/choice:deleteresponses', $context); 620 if ($candeleteall || $choice->allowupdate) { 621 622 // Check if we can delete our own responses. 623 if (!$candeleteall) { 624 $timenow = time(); 625 if (!empty($choice->timeclose) && ($timenow > $choice->timeclose)) { 626 throw new moodle_exception("expired", "choice", '', userdate($choice->timeclose)); 627 } 628 } 629 630 if (empty($params['responses'])) { 631 // No responses indicated so delete only my responses. 632 $todelete = array_keys(choice_get_my_response($choice)); 633 } else { 634 // Fill an array with the responses that can be deleted for this choice. 635 if ($candeleteall) { 636 // Teacher/managers can delete any. 637 $allowedresponses = array_keys(choice_get_all_responses($choice)); 638 } else { 639 // Students can delete only their own responses. 640 $allowedresponses = array_keys(choice_get_my_response($choice)); 641 } 642 643 $todelete = array(); 644 foreach ($params['responses'] as $response) { 645 if (!in_array($response, $allowedresponses)) { 646 $warnings[] = array( 647 'item' => 'response', 648 'itemid' => $response, 649 'warningcode' => 'nopermissions', 650 'message' => 'Invalid response id, the response does not exist or you are not allowed to delete it.' 651 ); 652 } else { 653 $todelete[] = $response; 654 } 655 } 656 } 657 658 $status = choice_delete_responses($todelete, $choice, $cm, $course); 659 } else { 660 // The user requires the capability to delete responses. 661 throw new required_capability_exception($context, 'mod/choice:deleteresponses', 'nopermissions', ''); 662 } 663 664 return array( 665 'status' => $status, 666 'warnings' => $warnings 667 ); 668 } 669 670 /** 671 * Describes the delete_choice_responses return value. 672 * 673 * @return external_multiple_structure 674 * @since Moodle 3.0 675 */ 676 public static function delete_choice_responses_returns() { 677 return new external_single_structure( 678 array( 679 'status' => new external_value(PARAM_BOOL, 'status, true if everything went right'), 680 'warnings' => new external_warnings(), 681 ) 682 ); 683 } 684 685 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body