Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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 * Lesson external API 19 * 20 * @package mod_lesson 21 * @category external 22 * @copyright 2017 Juan Leyva <juan@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 * @since Moodle 3.3 25 */ 26 27 defined('MOODLE_INTERNAL') || die; 28 29 require_once($CFG->libdir . '/externallib.php'); 30 require_once($CFG->dirroot . '/mod/lesson/locallib.php'); 31 32 use mod_lesson\external\lesson_summary_exporter; 33 34 /** 35 * Lesson external functions 36 * 37 * @package mod_lesson 38 * @category external 39 * @copyright 2017 Juan Leyva <juan@moodle.com> 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 * @since Moodle 3.3 42 */ 43 class mod_lesson_external extends external_api { 44 45 /** 46 * Return a lesson record ready for being exported. 47 * 48 * @param stdClass $lessonrecord lesson record 49 * @param string $password lesson password 50 * @return stdClass the lesson record ready for exporting. 51 */ 52 protected static function get_lesson_summary_for_exporter($lessonrecord, $password = '') { 53 global $USER; 54 55 $lesson = new lesson($lessonrecord); 56 $lesson->update_effective_access($USER->id); 57 $lessonavailable = $lesson->get_time_restriction_status() === false; 58 $lessonavailable = $lessonavailable && $lesson->get_password_restriction_status($password) === false; 59 $lessonavailable = $lessonavailable && $lesson->get_dependencies_restriction_status() === false; 60 $canmanage = $lesson->can_manage(); 61 62 if (!$canmanage && !$lessonavailable) { 63 $fields = array('intro', 'introfiles', 'mediafiles', 'practice', 'modattempts', 'usepassword', 64 'grade', 'custom', 'ongoing', 'usemaxgrade', 65 'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback', 'minquestions', 66 'maxpages', 'timelimit', 'retake', 'mediafile', 'mediaheight', 'mediawidth', 67 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif', 68 'progressbar'); 69 70 foreach ($fields as $field) { 71 unset($lessonrecord->{$field}); 72 } 73 } 74 75 // Fields only for managers. 76 if (!$canmanage) { 77 $fields = array('password', 'dependency', 'conditions', 'activitylink', 'available', 'deadline', 78 'timemodified', 'completionendreached', 'completiontimespent'); 79 80 foreach ($fields as $field) { 81 unset($lessonrecord->{$field}); 82 } 83 } 84 return $lessonrecord; 85 } 86 87 /** 88 * Describes the parameters for get_lessons_by_courses. 89 * 90 * @return external_function_parameters 91 * @since Moodle 3.3 92 */ 93 public static function get_lessons_by_courses_parameters() { 94 return new external_function_parameters ( 95 array( 96 'courseids' => new external_multiple_structure( 97 new external_value(PARAM_INT, 'course id'), 'Array of course ids', VALUE_DEFAULT, array() 98 ), 99 ) 100 ); 101 } 102 103 /** 104 * Returns a list of lessons in a provided list of courses, 105 * if no list is provided all lessons that the user can view will be returned. 106 * 107 * @param array $courseids Array of course ids 108 * @return array of lessons details 109 * @since Moodle 3.3 110 */ 111 public static function get_lessons_by_courses($courseids = array()) { 112 global $PAGE; 113 114 $warnings = array(); 115 $returnedlessons = array(); 116 117 $params = array( 118 'courseids' => $courseids, 119 ); 120 $params = self::validate_parameters(self::get_lessons_by_courses_parameters(), $params); 121 122 $mycourses = array(); 123 if (empty($params['courseids'])) { 124 $mycourses = enrol_get_my_courses(); 125 $params['courseids'] = array_keys($mycourses); 126 } 127 128 // Ensure there are courseids to loop through. 129 if (!empty($params['courseids'])) { 130 131 list($courses, $warnings) = external_util::validate_courses($params['courseids'], $mycourses); 132 133 // Get the lessons in this course, this function checks users visibility permissions. 134 // We can avoid then additional validate_context calls. 135 $lessons = get_all_instances_in_courses("lesson", $courses); 136 foreach ($lessons as $lessonrecord) { 137 $context = context_module::instance($lessonrecord->coursemodule); 138 139 // Remove fields added by get_all_instances_in_courses. 140 unset($lessonrecord->coursemodule, $lessonrecord->section, $lessonrecord->visible, $lessonrecord->groupmode, 141 $lessonrecord->groupingid); 142 143 $lessonrecord = self::get_lesson_summary_for_exporter($lessonrecord); 144 145 $exporter = new lesson_summary_exporter($lessonrecord, array('context' => $context)); 146 $lesson = $exporter->export($PAGE->get_renderer('core')); 147 $lesson->name = external_format_string($lesson->name, $context); 148 $returnedlessons[] = $lesson; 149 } 150 } 151 $result = array(); 152 $result['lessons'] = $returnedlessons; 153 $result['warnings'] = $warnings; 154 return $result; 155 } 156 157 /** 158 * Describes the get_lessons_by_courses return value. 159 * 160 * @return external_single_structure 161 * @since Moodle 3.3 162 */ 163 public static function get_lessons_by_courses_returns() { 164 return new external_single_structure( 165 array( 166 'lessons' => new external_multiple_structure( 167 lesson_summary_exporter::get_read_structure() 168 ), 169 'warnings' => new external_warnings(), 170 ) 171 ); 172 } 173 174 /** 175 * Utility function for validating a lesson. 176 * 177 * @param int $lessonid lesson instance id 178 * @return array array containing the lesson, course, context and course module objects 179 * @since Moodle 3.3 180 */ 181 protected static function validate_lesson($lessonid) { 182 global $DB, $USER; 183 184 // Request and permission validation. 185 $lessonrecord = $DB->get_record('lesson', array('id' => $lessonid), '*', MUST_EXIST); 186 list($course, $cm) = get_course_and_cm_from_instance($lessonrecord, 'lesson'); 187 188 $lesson = new lesson($lessonrecord, $cm, $course); 189 $lesson->update_effective_access($USER->id); 190 191 $context = $lesson->context; 192 self::validate_context($context); 193 194 return array($lesson, $course, $cm, $context, $lessonrecord); 195 } 196 197 /** 198 * Validates a new attempt. 199 * 200 * @param lesson $lesson lesson instance 201 * @param array $params request parameters 202 * @param boolean $return whether to return the errors or throw exceptions 203 * @return array the errors (if return set to true) 204 * @since Moodle 3.3 205 */ 206 protected static function validate_attempt(lesson $lesson, $params, $return = false) { 207 global $USER, $CFG; 208 209 $errors = array(); 210 211 // Avoid checkings for managers. 212 if ($lesson->can_manage()) { 213 return []; 214 } 215 216 // Dead line. 217 if ($timerestriction = $lesson->get_time_restriction_status()) { 218 $error = ["$timerestriction->reason" => userdate($timerestriction->time)]; 219 if (!$return) { 220 throw new moodle_exception(key($error), 'lesson', '', current($error)); 221 } 222 $errors[key($error)] = current($error); 223 } 224 225 // Password protected lesson code. 226 if ($passwordrestriction = $lesson->get_password_restriction_status($params['password'])) { 227 $error = ["passwordprotectedlesson" => external_format_string($lesson->name, $lesson->context->id)]; 228 if (!$return) { 229 throw new moodle_exception(key($error), 'lesson', '', current($error)); 230 } 231 $errors[key($error)] = current($error); 232 } 233 234 // Check for dependencies. 235 if ($dependenciesrestriction = $lesson->get_dependencies_restriction_status()) { 236 $errorhtmllist = implode(get_string('and', 'lesson') . ', ', $dependenciesrestriction->errors); 237 $error = ["completethefollowingconditions" => $dependenciesrestriction->dependentlesson->name . $errorhtmllist]; 238 if (!$return) { 239 throw new moodle_exception(key($error), 'lesson', '', current($error)); 240 } 241 $errors[key($error)] = current($error); 242 } 243 244 // To check only when no page is set (starting or continuing a lesson). 245 if (empty($params['pageid'])) { 246 // To avoid multiple calls, store the magic property firstpage. 247 $lessonfirstpage = $lesson->firstpage; 248 $lessonfirstpageid = $lessonfirstpage ? $lessonfirstpage->id : false; 249 250 // Check if the lesson does not have pages. 251 if (!$lessonfirstpageid) { 252 $error = ["lessonnotready2" => null]; 253 if (!$return) { 254 throw new moodle_exception(key($error), 'lesson'); 255 } 256 $errors[key($error)] = current($error); 257 } 258 259 // Get the number of retries (also referenced as attempts), and the last page seen. 260 $attemptscount = $lesson->count_user_retries($USER->id); 261 $lastpageseen = $lesson->get_last_page_seen($attemptscount); 262 263 // Check if the user left a timed session with no retakes. 264 if ($lastpageseen !== false && $lastpageseen != LESSON_EOL) { 265 if ($lesson->left_during_timed_session($attemptscount) && $lesson->timelimit && !$lesson->retake) { 266 $error = ["leftduringtimednoretake" => null]; 267 if (!$return) { 268 throw new moodle_exception(key($error), 'lesson'); 269 } 270 $errors[key($error)] = current($error); 271 } 272 } else if ($attemptscount > 0 && !$lesson->retake) { 273 // The user finished the lesson and no retakes are allowed. 274 $error = ["noretake" => null]; 275 if (!$return) { 276 throw new moodle_exception(key($error), 'lesson'); 277 } 278 $errors[key($error)] = current($error); 279 } 280 } else { 281 if (!$timers = $lesson->get_user_timers($USER->id, 'starttime DESC', '*', 0, 1)) { 282 $error = ["cannotfindtimer" => null]; 283 if (!$return) { 284 throw new moodle_exception(key($error), 'lesson'); 285 } 286 $errors[key($error)] = current($error); 287 } else { 288 $timer = current($timers); 289 if (!$lesson->check_time($timer)) { 290 $error = ["eolstudentoutoftime" => null]; 291 if (!$return) { 292 throw new moodle_exception(key($error), 'lesson'); 293 } 294 $errors[key($error)] = current($error); 295 } 296 297 // Check if the user want to review an attempt he just finished. 298 if (!empty($params['review'])) { 299 // Allow review only for attempts during active session time. 300 if ($timer->lessontime + $CFG->sessiontimeout > time()) { 301 $ntries = $lesson->count_user_retries($USER->id); 302 $ntries--; // Need to look at the old attempts. 303 if ($params['pageid'] == LESSON_EOL) { 304 if ($attempts = $lesson->get_attempts($ntries)) { 305 $lastattempt = end($attempts); 306 $USER->modattempts[$lesson->id] = $lastattempt->pageid; 307 } 308 } else { 309 if ($attempts = $lesson->get_attempts($ntries, false, $params['pageid'])) { 310 $lastattempt = end($attempts); 311 $USER->modattempts[$lesson->id] = $lastattempt; 312 } 313 } 314 } 315 316 if (!isset($USER->modattempts[$lesson->id])) { 317 $error = ["studentoutoftimeforreview" => null]; 318 if (!$return) { 319 throw new moodle_exception(key($error), 'lesson'); 320 } 321 $errors[key($error)] = current($error); 322 } 323 } 324 } 325 } 326 327 return $errors; 328 } 329 330 /** 331 * Describes the parameters for get_lesson_access_information. 332 * 333 * @return external_function_parameters 334 * @since Moodle 3.3 335 */ 336 public static function get_lesson_access_information_parameters() { 337 return new external_function_parameters ( 338 array( 339 'lessonid' => new external_value(PARAM_INT, 'lesson instance id') 340 ) 341 ); 342 } 343 344 /** 345 * Return access information for a given lesson. 346 * 347 * @param int $lessonid lesson instance id 348 * @return array of warnings and the access information 349 * @since Moodle 3.3 350 * @throws moodle_exception 351 */ 352 public static function get_lesson_access_information($lessonid) { 353 global $DB, $USER; 354 355 $warnings = array(); 356 357 $params = array( 358 'lessonid' => $lessonid 359 ); 360 $params = self::validate_parameters(self::get_lesson_access_information_parameters(), $params); 361 362 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 363 364 $result = array(); 365 // Capabilities first. 366 $result['canmanage'] = $lesson->can_manage(); 367 $result['cangrade'] = has_capability('mod/lesson:grade', $context); 368 $result['canviewreports'] = has_capability('mod/lesson:viewreports', $context); 369 370 // Status information. 371 $result['reviewmode'] = $lesson->is_in_review_mode(); 372 $result['attemptscount'] = $lesson->count_user_retries($USER->id); 373 $lastpageseen = $lesson->get_last_page_seen($result['attemptscount']); 374 $result['lastpageseen'] = ($lastpageseen !== false) ? $lastpageseen : 0; 375 $result['leftduringtimedsession'] = $lesson->left_during_timed_session($result['attemptscount']); 376 // To avoid multiple calls, store the magic property firstpage. 377 $lessonfirstpage = $lesson->firstpage; 378 $result['firstpageid'] = $lessonfirstpage ? $lessonfirstpage->id : 0; 379 380 // Access restrictions now, we emulate a new attempt access to get the possible warnings. 381 $result['preventaccessreasons'] = []; 382 $validationerrors = self::validate_attempt($lesson, ['password' => ''], true); 383 foreach ($validationerrors as $reason => $data) { 384 $result['preventaccessreasons'][] = [ 385 'reason' => $reason, 386 'data' => $data, 387 'message' => get_string($reason, 'lesson', $data), 388 ]; 389 } 390 $result['warnings'] = $warnings; 391 return $result; 392 } 393 394 /** 395 * Describes the get_lesson_access_information return value. 396 * 397 * @return external_single_structure 398 * @since Moodle 3.3 399 */ 400 public static function get_lesson_access_information_returns() { 401 return new external_single_structure( 402 array( 403 'canmanage' => new external_value(PARAM_BOOL, 'Whether the user can manage the lesson or not.'), 404 'cangrade' => new external_value(PARAM_BOOL, 'Whether the user can grade the lesson or not.'), 405 'canviewreports' => new external_value(PARAM_BOOL, 'Whether the user can view the lesson reports or not.'), 406 'reviewmode' => new external_value(PARAM_BOOL, 'Whether the lesson is in review mode for the current user.'), 407 'attemptscount' => new external_value(PARAM_INT, 'The number of attempts done by the user.'), 408 'lastpageseen' => new external_value(PARAM_INT, 'The last page seen id.'), 409 'leftduringtimedsession' => new external_value(PARAM_BOOL, 'Whether the user left during a timed session.'), 410 'firstpageid' => new external_value(PARAM_INT, 'The lesson first page id.'), 411 'preventaccessreasons' => new external_multiple_structure( 412 new external_single_structure( 413 array( 414 'reason' => new external_value(PARAM_ALPHANUMEXT, 'Reason lang string code'), 415 'data' => new external_value(PARAM_RAW, 'Additional data'), 416 'message' => new external_value(PARAM_RAW, 'Complete html message'), 417 ), 418 'The reasons why the user cannot attempt the lesson' 419 ) 420 ), 421 'warnings' => new external_warnings(), 422 ) 423 ); 424 } 425 426 /** 427 * Describes the parameters for view_lesson. 428 * 429 * @return external_function_parameters 430 * @since Moodle 3.3 431 */ 432 public static function view_lesson_parameters() { 433 return new external_function_parameters ( 434 array( 435 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 436 'password' => new external_value(PARAM_RAW, 'lesson password', VALUE_DEFAULT, ''), 437 ) 438 ); 439 } 440 441 /** 442 * Trigger the course module viewed event and update the module completion status. 443 * 444 * @param int $lessonid lesson instance id 445 * @param string $password optional password (the lesson may be protected) 446 * @return array of warnings and status result 447 * @since Moodle 3.3 448 * @throws moodle_exception 449 */ 450 public static function view_lesson($lessonid, $password = '') { 451 global $DB; 452 453 $params = array('lessonid' => $lessonid, 'password' => $password); 454 $params = self::validate_parameters(self::view_lesson_parameters(), $params); 455 $warnings = array(); 456 457 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 458 self::validate_attempt($lesson, $params); 459 460 $lesson->set_module_viewed(); 461 462 $result = array(); 463 $result['status'] = true; 464 $result['warnings'] = $warnings; 465 return $result; 466 } 467 468 /** 469 * Describes the view_lesson return value. 470 * 471 * @return external_single_structure 472 * @since Moodle 3.3 473 */ 474 public static function view_lesson_returns() { 475 return new external_single_structure( 476 array( 477 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 478 'warnings' => new external_warnings(), 479 ) 480 ); 481 } 482 483 /** 484 * Check if the current user can retrieve lesson information (grades, attempts) about the given user. 485 * 486 * @param int $userid the user to check 487 * @param stdClass $course course object 488 * @param stdClass $cm cm object 489 * @param stdClass $context context object 490 * @throws moodle_exception 491 * @since Moodle 3.3 492 */ 493 protected static function check_can_view_user_data($userid, $course, $cm, $context) { 494 $user = core_user::get_user($userid, '*', MUST_EXIST); 495 core_user::require_active_user($user); 496 // Check permissions and that if users share group (if groups enabled). 497 require_capability('mod/lesson:viewreports', $context); 498 if (!groups_user_groups_visible($course, $user->id, $cm)) { 499 throw new moodle_exception('notingroup'); 500 } 501 } 502 503 /** 504 * Describes the parameters for get_questions_attempts. 505 * 506 * @return external_function_parameters 507 * @since Moodle 3.3 508 */ 509 public static function get_questions_attempts_parameters() { 510 return new external_function_parameters ( 511 array( 512 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 513 'attempt' => new external_value(PARAM_INT, 'lesson attempt number'), 514 'correct' => new external_value(PARAM_BOOL, 'only fetch correct attempts', VALUE_DEFAULT, false), 515 'pageid' => new external_value(PARAM_INT, 'only fetch attempts at the given page', VALUE_DEFAULT, null), 516 'userid' => new external_value(PARAM_INT, 'only fetch attempts of the given user', VALUE_DEFAULT, null), 517 ) 518 ); 519 } 520 521 /** 522 * Return the list of page question attempts in a given lesson. 523 * 524 * @param int $lessonid lesson instance id 525 * @param int $attempt the lesson attempt number 526 * @param bool $correct only fetch correct attempts 527 * @param int $pageid only fetch attempts at the given page 528 * @param int $userid only fetch attempts of the given user 529 * @return array of warnings and page attempts 530 * @since Moodle 3.3 531 * @throws moodle_exception 532 */ 533 public static function get_questions_attempts($lessonid, $attempt, $correct = false, $pageid = null, $userid = null) { 534 global $DB, $USER; 535 536 $params = array( 537 'lessonid' => $lessonid, 538 'attempt' => $attempt, 539 'correct' => $correct, 540 'pageid' => $pageid, 541 'userid' => $userid, 542 ); 543 $params = self::validate_parameters(self::get_questions_attempts_parameters(), $params); 544 $warnings = array(); 545 546 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 547 548 // Default value for userid. 549 if (empty($params['userid'])) { 550 $params['userid'] = $USER->id; 551 } 552 553 // Extra checks so only users with permissions can view other users attempts. 554 if ($USER->id != $params['userid']) { 555 self::check_can_view_user_data($params['userid'], $course, $cm, $context); 556 } 557 558 $result = array(); 559 $result['attempts'] = $lesson->get_attempts($params['attempt'], $params['correct'], $params['pageid'], $params['userid']); 560 $result['warnings'] = $warnings; 561 return $result; 562 } 563 564 /** 565 * Describes the get_questions_attempts return value. 566 * 567 * @return external_single_structure 568 * @since Moodle 3.3 569 */ 570 public static function get_questions_attempts_returns() { 571 return new external_single_structure( 572 array( 573 'attempts' => new external_multiple_structure( 574 new external_single_structure( 575 array( 576 'id' => new external_value(PARAM_INT, 'The attempt id'), 577 'lessonid' => new external_value(PARAM_INT, 'The attempt lessonid'), 578 'pageid' => new external_value(PARAM_INT, 'The attempt pageid'), 579 'userid' => new external_value(PARAM_INT, 'The user who did the attempt'), 580 'answerid' => new external_value(PARAM_INT, 'The attempt answerid'), 581 'retry' => new external_value(PARAM_INT, 'The lesson attempt number'), 582 'correct' => new external_value(PARAM_INT, 'If it was the correct answer'), 583 'useranswer' => new external_value(PARAM_RAW, 'The complete user answer'), 584 'timeseen' => new external_value(PARAM_INT, 'The time the question was seen'), 585 ), 586 'The question page attempts' 587 ) 588 ), 589 'warnings' => new external_warnings(), 590 ) 591 ); 592 } 593 594 /** 595 * Describes the parameters for get_user_grade. 596 * 597 * @return external_function_parameters 598 * @since Moodle 3.3 599 */ 600 public static function get_user_grade_parameters() { 601 return new external_function_parameters ( 602 array( 603 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 604 'userid' => new external_value(PARAM_INT, 'the user id (empty for current user)', VALUE_DEFAULT, null), 605 ) 606 ); 607 } 608 609 /** 610 * Return the final grade in the lesson for the given user. 611 * 612 * @param int $lessonid lesson instance id 613 * @param int $userid only fetch grades of this user 614 * @return array of warnings and page attempts 615 * @since Moodle 3.3 616 * @throws moodle_exception 617 */ 618 public static function get_user_grade($lessonid, $userid = null) { 619 global $CFG, $USER; 620 require_once($CFG->libdir . '/gradelib.php'); 621 622 $params = array( 623 'lessonid' => $lessonid, 624 'userid' => $userid, 625 ); 626 $params = self::validate_parameters(self::get_user_grade_parameters(), $params); 627 $warnings = array(); 628 629 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 630 631 // Default value for userid. 632 if (empty($params['userid'])) { 633 $params['userid'] = $USER->id; 634 } 635 636 // Extra checks so only users with permissions can view other users attempts. 637 if ($USER->id != $params['userid']) { 638 self::check_can_view_user_data($params['userid'], $course, $cm, $context); 639 } 640 641 $grade = null; 642 $formattedgrade = null; 643 $grades = lesson_get_user_grades($lesson, $params['userid']); 644 if (!empty($grades)) { 645 $grade = $grades[$params['userid']]->rawgrade; 646 $params = array( 647 'itemtype' => 'mod', 648 'itemmodule' => 'lesson', 649 'iteminstance' => $lesson->id, 650 'courseid' => $course->id, 651 'itemnumber' => 0 652 ); 653 $gradeitem = grade_item::fetch($params); 654 $formattedgrade = grade_format_gradevalue($grade, $gradeitem); 655 } 656 657 $result = array(); 658 $result['grade'] = $grade; 659 $result['formattedgrade'] = $formattedgrade; 660 $result['warnings'] = $warnings; 661 return $result; 662 } 663 664 /** 665 * Describes the get_user_grade return value. 666 * 667 * @return external_single_structure 668 * @since Moodle 3.3 669 */ 670 public static function get_user_grade_returns() { 671 return new external_single_structure( 672 array( 673 'grade' => new external_value(PARAM_FLOAT, 'The lesson final raw grade'), 674 'formattedgrade' => new external_value(PARAM_RAW, 'The lesson final grade formatted'), 675 'warnings' => new external_warnings(), 676 ) 677 ); 678 } 679 680 /** 681 * Describes an attempt grade structure. 682 * 683 * @param int $required if the structure is required or optional 684 * @return external_single_structure the structure 685 * @since Moodle 3.3 686 */ 687 protected static function get_user_attempt_grade_structure($required = VALUE_REQUIRED) { 688 $data = array( 689 'nquestions' => new external_value(PARAM_INT, 'Number of questions answered'), 690 'attempts' => new external_value(PARAM_INT, 'Number of question attempts'), 691 'total' => new external_value(PARAM_FLOAT, 'Max points possible'), 692 'earned' => new external_value(PARAM_FLOAT, 'Points earned by student'), 693 'grade' => new external_value(PARAM_FLOAT, 'Calculated percentage grade'), 694 'nmanual' => new external_value(PARAM_INT, 'Number of manually graded questions'), 695 'manualpoints' => new external_value(PARAM_FLOAT, 'Point value for manually graded questions'), 696 ); 697 return new external_single_structure( 698 $data, 'Attempt grade', $required 699 ); 700 } 701 702 /** 703 * Describes the parameters for get_user_attempt_grade. 704 * 705 * @return external_function_parameters 706 * @since Moodle 3.3 707 */ 708 public static function get_user_attempt_grade_parameters() { 709 return new external_function_parameters ( 710 array( 711 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 712 'lessonattempt' => new external_value(PARAM_INT, 'lesson attempt number'), 713 'userid' => new external_value(PARAM_INT, 'the user id (empty for current user)', VALUE_DEFAULT, null), 714 ) 715 ); 716 } 717 718 /** 719 * Return grade information in the attempt for a given user. 720 * 721 * @param int $lessonid lesson instance id 722 * @param int $lessonattempt lesson attempt number 723 * @param int $userid only fetch attempts of the given user 724 * @return array of warnings and page attempts 725 * @since Moodle 3.3 726 * @throws moodle_exception 727 */ 728 public static function get_user_attempt_grade($lessonid, $lessonattempt, $userid = null) { 729 global $CFG, $USER; 730 require_once($CFG->libdir . '/gradelib.php'); 731 732 $params = array( 733 'lessonid' => $lessonid, 734 'lessonattempt' => $lessonattempt, 735 'userid' => $userid, 736 ); 737 $params = self::validate_parameters(self::get_user_attempt_grade_parameters(), $params); 738 $warnings = array(); 739 740 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 741 742 // Default value for userid. 743 if (empty($params['userid'])) { 744 $params['userid'] = $USER->id; 745 } 746 747 // Extra checks so only users with permissions can view other users attempts. 748 if ($USER->id != $params['userid']) { 749 self::check_can_view_user_data($params['userid'], $course, $cm, $context); 750 } 751 752 $result = array(); 753 $result['grade'] = (array) lesson_grade($lesson, $params['lessonattempt'], $params['userid']); 754 $result['warnings'] = $warnings; 755 return $result; 756 } 757 758 /** 759 * Describes the get_user_attempt_grade return value. 760 * 761 * @return external_single_structure 762 * @since Moodle 3.3 763 */ 764 public static function get_user_attempt_grade_returns() { 765 return new external_single_structure( 766 array( 767 'grade' => self::get_user_attempt_grade_structure(), 768 'warnings' => new external_warnings(), 769 ) 770 ); 771 } 772 773 /** 774 * Describes the parameters for get_content_pages_viewed. 775 * 776 * @return external_function_parameters 777 * @since Moodle 3.3 778 */ 779 public static function get_content_pages_viewed_parameters() { 780 return new external_function_parameters ( 781 array( 782 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 783 'lessonattempt' => new external_value(PARAM_INT, 'lesson attempt number'), 784 'userid' => new external_value(PARAM_INT, 'the user id (empty for current user)', VALUE_DEFAULT, null), 785 ) 786 ); 787 } 788 789 /** 790 * Return the list of content pages viewed by a user during a lesson attempt. 791 * 792 * @param int $lessonid lesson instance id 793 * @param int $lessonattempt lesson attempt number 794 * @param int $userid only fetch attempts of the given user 795 * @return array of warnings and page attempts 796 * @since Moodle 3.3 797 * @throws moodle_exception 798 */ 799 public static function get_content_pages_viewed($lessonid, $lessonattempt, $userid = null) { 800 global $USER; 801 802 $params = array( 803 'lessonid' => $lessonid, 804 'lessonattempt' => $lessonattempt, 805 'userid' => $userid, 806 ); 807 $params = self::validate_parameters(self::get_content_pages_viewed_parameters(), $params); 808 $warnings = array(); 809 810 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 811 812 // Default value for userid. 813 if (empty($params['userid'])) { 814 $params['userid'] = $USER->id; 815 } 816 817 // Extra checks so only users with permissions can view other users attempts. 818 if ($USER->id != $params['userid']) { 819 self::check_can_view_user_data($params['userid'], $course, $cm, $context); 820 } 821 822 $pages = $lesson->get_content_pages_viewed($params['lessonattempt'], $params['userid']); 823 824 $result = array(); 825 $result['pages'] = $pages; 826 $result['warnings'] = $warnings; 827 return $result; 828 } 829 830 /** 831 * Describes the get_content_pages_viewed return value. 832 * 833 * @return external_single_structure 834 * @since Moodle 3.3 835 */ 836 public static function get_content_pages_viewed_returns() { 837 return new external_single_structure( 838 array( 839 'pages' => new external_multiple_structure( 840 new external_single_structure( 841 array( 842 'id' => new external_value(PARAM_INT, 'The attempt id.'), 843 'lessonid' => new external_value(PARAM_INT, 'The lesson id.'), 844 'pageid' => new external_value(PARAM_INT, 'The page id.'), 845 'userid' => new external_value(PARAM_INT, 'The user who viewed the page.'), 846 'retry' => new external_value(PARAM_INT, 'The lesson attempt number.'), 847 'flag' => new external_value(PARAM_INT, '1 if the next page was calculated randomly.'), 848 'timeseen' => new external_value(PARAM_INT, 'The time the page was seen.'), 849 'nextpageid' => new external_value(PARAM_INT, 'The next page chosen id.'), 850 ), 851 'The content pages viewed.' 852 ) 853 ), 854 'warnings' => new external_warnings(), 855 ) 856 ); 857 } 858 859 /** 860 * Describes the parameters for get_user_timers. 861 * 862 * @return external_function_parameters 863 * @since Moodle 3.3 864 */ 865 public static function get_user_timers_parameters() { 866 return new external_function_parameters ( 867 array( 868 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 869 'userid' => new external_value(PARAM_INT, 'the user id (empty for current user)', VALUE_DEFAULT, null), 870 ) 871 ); 872 } 873 874 /** 875 * Return the timers in the current lesson for the given user. 876 * 877 * @param int $lessonid lesson instance id 878 * @param int $userid only fetch timers of the given user 879 * @return array of warnings and timers 880 * @since Moodle 3.3 881 * @throws moodle_exception 882 */ 883 public static function get_user_timers($lessonid, $userid = null) { 884 global $USER; 885 886 $params = array( 887 'lessonid' => $lessonid, 888 'userid' => $userid, 889 ); 890 $params = self::validate_parameters(self::get_user_timers_parameters(), $params); 891 $warnings = array(); 892 893 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 894 895 // Default value for userid. 896 if (empty($params['userid'])) { 897 $params['userid'] = $USER->id; 898 } 899 900 // Extra checks so only users with permissions can view other users attempts. 901 if ($USER->id != $params['userid']) { 902 self::check_can_view_user_data($params['userid'], $course, $cm, $context); 903 } 904 905 $timers = $lesson->get_user_timers($params['userid']); 906 907 $result = array(); 908 $result['timers'] = $timers; 909 $result['warnings'] = $warnings; 910 return $result; 911 } 912 913 /** 914 * Describes the get_user_timers return value. 915 * 916 * @return external_single_structure 917 * @since Moodle 3.3 918 */ 919 public static function get_user_timers_returns() { 920 return new external_single_structure( 921 array( 922 'timers' => new external_multiple_structure( 923 new external_single_structure( 924 array( 925 'id' => new external_value(PARAM_INT, 'The attempt id'), 926 'lessonid' => new external_value(PARAM_INT, 'The lesson id'), 927 'userid' => new external_value(PARAM_INT, 'The user id'), 928 'starttime' => new external_value(PARAM_INT, 'First access time for a new timer session'), 929 'lessontime' => new external_value(PARAM_INT, 'Last access time to the lesson during the timer session'), 930 'completed' => new external_value(PARAM_INT, 'If the lesson for this timer was completed'), 931 'timemodifiedoffline' => new external_value(PARAM_INT, 'Last modified time via webservices.'), 932 ), 933 'The timers' 934 ) 935 ), 936 'warnings' => new external_warnings(), 937 ) 938 ); 939 } 940 941 /** 942 * Describes the external structure for a lesson page. 943 * 944 * @return external_single_structure 945 * @since Moodle 3.3 946 */ 947 protected static function get_page_structure($required = VALUE_REQUIRED) { 948 return new external_single_structure( 949 array( 950 'id' => new external_value(PARAM_INT, 'The id of this lesson page'), 951 'lessonid' => new external_value(PARAM_INT, 'The id of the lesson this page belongs to'), 952 'prevpageid' => new external_value(PARAM_INT, 'The id of the page before this one'), 953 'nextpageid' => new external_value(PARAM_INT, 'The id of the next page in the page sequence'), 954 'qtype' => new external_value(PARAM_INT, 'Identifies the page type of this page'), 955 'qoption' => new external_value(PARAM_INT, 'Used to record page type specific options'), 956 'layout' => new external_value(PARAM_INT, 'Used to record page specific layout selections'), 957 'display' => new external_value(PARAM_INT, 'Used to record page specific display selections'), 958 'timecreated' => new external_value(PARAM_INT, 'Timestamp for when the page was created'), 959 'timemodified' => new external_value(PARAM_INT, 'Timestamp for when the page was last modified'), 960 'title' => new external_value(PARAM_RAW, 'The title of this page', VALUE_OPTIONAL), 961 'contents' => new external_value(PARAM_RAW, 'The contents of this page', VALUE_OPTIONAL), 962 'contentsformat' => new external_format_value('contents', VALUE_OPTIONAL), 963 'displayinmenublock' => new external_value(PARAM_BOOL, 'Toggles display in the left menu block'), 964 'type' => new external_value(PARAM_INT, 'The type of the page [question | structure]'), 965 'typeid' => new external_value(PARAM_INT, 'The unique identifier for the page type'), 966 'typestring' => new external_value(PARAM_RAW, 'The string that describes this page type'), 967 ), 968 'Page fields', $required 969 ); 970 } 971 972 /** 973 * Returns the fields of a page object 974 * @param lesson_page $page the lesson page 975 * @param bool $returncontents whether to return the page title and contents 976 * @return stdClass the fields matching the external page structure 977 * @since Moodle 3.3 978 */ 979 protected static function get_page_fields(lesson_page $page, $returncontents = false) { 980 $lesson = $page->lesson; 981 $context = $lesson->context; 982 983 $pagedata = new stdClass; // Contains the data that will be returned by the WS. 984 985 // Return the visible data. 986 $visibleproperties = array('id', 'lessonid', 'prevpageid', 'nextpageid', 'qtype', 'qoption', 'layout', 'display', 987 'displayinmenublock', 'type', 'typeid', 'typestring', 'timecreated', 'timemodified'); 988 foreach ($visibleproperties as $prop) { 989 $pagedata->{$prop} = $page->{$prop}; 990 } 991 992 // Check if we can see title (contents required custom rendering, we won't returning it here @see get_page_data). 993 $canmanage = $lesson->can_manage(); 994 // If we are managers or the menu block is enabled and is a content page visible always return contents. 995 if ($returncontents || $canmanage || (lesson_displayleftif($lesson) && $page->displayinmenublock && $page->display)) { 996 $pagedata->title = external_format_string($page->title, $context->id); 997 998 $options = array('noclean' => true); 999 list($pagedata->contents, $pagedata->contentsformat) = 1000 external_format_text($page->contents, $page->contentsformat, $context->id, 'mod_lesson', 'page_contents', $page->id, 1001 $options); 1002 1003 } 1004 return $pagedata; 1005 } 1006 1007 /** 1008 * Describes the parameters for get_pages. 1009 * 1010 * @return external_function_parameters 1011 * @since Moodle 3.3 1012 */ 1013 public static function get_pages_parameters() { 1014 return new external_function_parameters ( 1015 array( 1016 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 1017 'password' => new external_value(PARAM_RAW, 'optional password (the lesson may be protected)', VALUE_DEFAULT, ''), 1018 ) 1019 ); 1020 } 1021 1022 /** 1023 * Return the list of pages in a lesson (based on the user permissions). 1024 * 1025 * @param int $lessonid lesson instance id 1026 * @param string $password optional password (the lesson may be protected) 1027 * @return array of warnings and status result 1028 * @since Moodle 3.3 1029 * @throws moodle_exception 1030 */ 1031 public static function get_pages($lessonid, $password = '') { 1032 1033 $params = array('lessonid' => $lessonid, 'password' => $password); 1034 $params = self::validate_parameters(self::get_pages_parameters(), $params); 1035 $warnings = array(); 1036 1037 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 1038 self::validate_attempt($lesson, $params); 1039 1040 $lessonpages = $lesson->load_all_pages(); 1041 $pages = array(); 1042 1043 foreach ($lessonpages as $page) { 1044 $pagedata = new stdClass(); 1045 1046 // Get the page object fields. 1047 $pagedata->page = self::get_page_fields($page); 1048 1049 // Now, calculate the file area files (maybe we need to download a lesson for offline usage). 1050 $pagedata->filescount = 0; 1051 $pagedata->filessizetotal = 0; 1052 $files = $page->get_files(false); // Get files excluding directories. 1053 foreach ($files as $file) { 1054 $pagedata->filescount++; 1055 $pagedata->filessizetotal += $file->get_filesize(); 1056 } 1057 1058 // Now the possible answers and page jumps ids. 1059 $pagedata->answerids = array(); 1060 $pagedata->jumps = array(); 1061 $answers = $page->get_answers(); 1062 foreach ($answers as $answer) { 1063 $pagedata->answerids[] = $answer->id; 1064 $pagedata->jumps[] = $answer->jumpto; 1065 $files = $answer->get_files(false); // Get files excluding directories. 1066 foreach ($files as $file) { 1067 $pagedata->filescount++; 1068 $pagedata->filessizetotal += $file->get_filesize(); 1069 } 1070 } 1071 $pages[] = $pagedata; 1072 } 1073 1074 $result = array(); 1075 $result['pages'] = $pages; 1076 $result['warnings'] = $warnings; 1077 return $result; 1078 } 1079 1080 /** 1081 * Describes the get_pages return value. 1082 * 1083 * @return external_single_structure 1084 * @since Moodle 3.3 1085 */ 1086 public static function get_pages_returns() { 1087 return new external_single_structure( 1088 array( 1089 'pages' => new external_multiple_structure( 1090 new external_single_structure( 1091 array( 1092 'page' => self::get_page_structure(), 1093 'answerids' => new external_multiple_structure( 1094 new external_value(PARAM_INT, 'Answer id'), 'List of answers ids (empty for content pages in Moodle 1.9)' 1095 ), 1096 'jumps' => new external_multiple_structure( 1097 new external_value(PARAM_INT, 'Page to jump id'), 'List of possible page jumps' 1098 ), 1099 'filescount' => new external_value(PARAM_INT, 'The total number of files attached to the page'), 1100 'filessizetotal' => new external_value(PARAM_INT, 'The total size of the files'), 1101 ), 1102 'The lesson pages' 1103 ) 1104 ), 1105 'warnings' => new external_warnings(), 1106 ) 1107 ); 1108 } 1109 1110 /** 1111 * Describes the parameters for launch_attempt. 1112 * 1113 * @return external_function_parameters 1114 * @since Moodle 3.3 1115 */ 1116 public static function launch_attempt_parameters() { 1117 return new external_function_parameters ( 1118 array( 1119 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 1120 'password' => new external_value(PARAM_RAW, 'optional password (the lesson may be protected)', VALUE_DEFAULT, ''), 1121 'pageid' => new external_value(PARAM_INT, 'page id to continue from (only when continuing an attempt)', VALUE_DEFAULT, 0), 1122 'review' => new external_value(PARAM_BOOL, 'if we want to review just after finishing', VALUE_DEFAULT, false), 1123 ) 1124 ); 1125 } 1126 1127 /** 1128 * Return lesson messages formatted according the external_messages structure 1129 * 1130 * @param lesson $lesson lesson instance 1131 * @return array messages formatted 1132 * @since Moodle 3.3 1133 */ 1134 protected static function format_lesson_messages($lesson) { 1135 $messages = array(); 1136 foreach ($lesson->messages as $message) { 1137 $messages[] = array( 1138 'message' => $message[0], 1139 'type' => $message[1], 1140 ); 1141 } 1142 return $messages; 1143 } 1144 1145 /** 1146 * Return a external structure representing messages. 1147 * 1148 * @return external_multiple_structure messages structure 1149 * @since Moodle 3.3 1150 */ 1151 protected static function external_messages() { 1152 return new external_multiple_structure( 1153 new external_single_structure( 1154 array( 1155 'message' => new external_value(PARAM_RAW, 'Message.'), 1156 'type' => new external_value(PARAM_ALPHANUMEXT, 'Message type: usually a CSS identifier like: 1157 success, info, warning, error, notifyproblem, notifyerror, notifytiny, notifysuccess') 1158 ), 'The lesson generated messages' 1159 ) 1160 ); 1161 } 1162 1163 /** 1164 * Starts a new attempt or continues an existing one. 1165 * 1166 * @param int $lessonid lesson instance id 1167 * @param string $password optional password (the lesson may be protected) 1168 * @param int $pageid page id to continue from (only when continuing an attempt) 1169 * @param bool $review if we want to review just after finishing 1170 * @return array of warnings and status result 1171 * @since Moodle 3.3 1172 * @throws moodle_exception 1173 */ 1174 public static function launch_attempt($lessonid, $password = '', $pageid = 0, $review = false) { 1175 global $CFG, $USER; 1176 1177 $params = array('lessonid' => $lessonid, 'password' => $password, 'pageid' => $pageid, 'review' => $review); 1178 $params = self::validate_parameters(self::launch_attempt_parameters(), $params); 1179 $warnings = array(); 1180 1181 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 1182 self::validate_attempt($lesson, $params); 1183 1184 $newpageid = 0; 1185 // Starting a new lesson attempt. 1186 if (empty($params['pageid'])) { 1187 // Check if there is a recent timer created during the active session. 1188 $alreadystarted = false; 1189 if ($timers = $lesson->get_user_timers($USER->id, 'starttime DESC', '*', 0, 1)) { 1190 $timer = array_shift($timers); 1191 $endtime = $lesson->timelimit > 0 ? min($CFG->sessiontimeout, $lesson->timelimit) : $CFG->sessiontimeout; 1192 if (!$timer->completed && $timer->starttime > time() - $endtime) { 1193 $alreadystarted = true; 1194 } 1195 } 1196 if (!$alreadystarted && !$lesson->can_manage()) { 1197 $lesson->start_timer(); 1198 } 1199 } else { 1200 if ($params['pageid'] == LESSON_EOL) { 1201 throw new moodle_exception('endoflesson', 'lesson'); 1202 } 1203 $timer = $lesson->update_timer(true, true); 1204 if (!$lesson->check_time($timer)) { 1205 throw new moodle_exception('eolstudentoutoftime', 'lesson'); 1206 } 1207 } 1208 $messages = self::format_lesson_messages($lesson); 1209 1210 $result = array( 1211 'status' => true, 1212 'messages' => $messages, 1213 'warnings' => $warnings, 1214 ); 1215 return $result; 1216 } 1217 1218 /** 1219 * Describes the launch_attempt return value. 1220 * 1221 * @return external_single_structure 1222 * @since Moodle 3.3 1223 */ 1224 public static function launch_attempt_returns() { 1225 return new external_single_structure( 1226 array( 1227 'messages' => self::external_messages(), 1228 'warnings' => new external_warnings(), 1229 ) 1230 ); 1231 } 1232 1233 /** 1234 * Describes the parameters for get_page_data. 1235 * 1236 * @return external_function_parameters 1237 * @since Moodle 3.3 1238 */ 1239 public static function get_page_data_parameters() { 1240 return new external_function_parameters ( 1241 array( 1242 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 1243 'pageid' => new external_value(PARAM_INT, 'the page id'), 1244 'password' => new external_value(PARAM_RAW, 'optional password (the lesson may be protected)', VALUE_DEFAULT, ''), 1245 'review' => new external_value(PARAM_BOOL, 'if we want to review just after finishing (1 hour margin)', 1246 VALUE_DEFAULT, false), 1247 'returncontents' => new external_value(PARAM_BOOL, 'if we must return the complete page contents once rendered', 1248 VALUE_DEFAULT, false), 1249 ) 1250 ); 1251 } 1252 1253 /** 1254 * Return information of a given page, including its contents. 1255 * 1256 * @param int $lessonid lesson instance id 1257 * @param int $pageid page id 1258 * @param string $password optional password (the lesson may be protected) 1259 * @param bool $review if we want to review just after finishing (1 hour margin) 1260 * @param bool $returncontents if we must return the complete page contents once rendered 1261 * @return array of warnings and status result 1262 * @since Moodle 3.3 1263 * @throws moodle_exception 1264 */ 1265 public static function get_page_data($lessonid, $pageid, $password = '', $review = false, $returncontents = false) { 1266 global $PAGE, $USER; 1267 1268 $params = array('lessonid' => $lessonid, 'password' => $password, 'pageid' => $pageid, 'review' => $review, 1269 'returncontents' => $returncontents); 1270 $params = self::validate_parameters(self::get_page_data_parameters(), $params); 1271 1272 $warnings = $contentfiles = $answerfiles = $responsefiles = $answers = array(); 1273 $pagecontent = $ongoingscore = ''; 1274 $progress = $pagedata = null; 1275 1276 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 1277 self::validate_attempt($lesson, $params); 1278 1279 $pageid = $params['pageid']; 1280 1281 // This is called if a student leaves during a lesson. 1282 if ($pageid == LESSON_UNSEENBRANCHPAGE) { 1283 $pageid = lesson_unseen_question_jump($lesson, $USER->id, $pageid); 1284 } 1285 1286 if ($pageid != LESSON_EOL) { 1287 $reviewmode = $lesson->is_in_review_mode(); 1288 $lessonoutput = $PAGE->get_renderer('mod_lesson'); 1289 // Prepare page contents avoiding redirections. 1290 list($pageid, $page, $pagecontent) = $lesson->prepare_page_and_contents($pageid, $lessonoutput, $reviewmode, false); 1291 1292 if ($pageid > 0) { 1293 1294 $pagedata = self::get_page_fields($page, true); 1295 1296 // Files. 1297 $contentfiles = external_util::get_area_files($context->id, 'mod_lesson', 'page_contents', $page->id); 1298 1299 // Answers. 1300 $answers = array(); 1301 $pageanswers = $page->get_answers(); 1302 foreach ($pageanswers as $a) { 1303 $answer = array( 1304 'id' => $a->id, 1305 'answerfiles' => external_util::get_area_files($context->id, 'mod_lesson', 'page_answers', $a->id), 1306 'responsefiles' => external_util::get_area_files($context->id, 'mod_lesson', 'page_responses', $a->id), 1307 ); 1308 // For managers, return all the information (including correct answers, jumps). 1309 // If the teacher enabled offline attempts, this information will be downloaded too. 1310 if ($lesson->can_manage() || $lesson->allowofflineattempts) { 1311 $extraproperties = array('jumpto', 'grade', 'score', 'flags', 'timecreated', 'timemodified'); 1312 foreach ($extraproperties as $prop) { 1313 $answer[$prop] = $a->{$prop}; 1314 } 1315 1316 $options = array('noclean' => true); 1317 list($answer['answer'], $answer['answerformat']) = 1318 external_format_text($a->answer, $a->answerformat, $context->id, 'mod_lesson', 'page_answers', $a->id, 1319 $options); 1320 list($answer['response'], $answer['responseformat']) = 1321 external_format_text($a->response, $a->responseformat, $context->id, 'mod_lesson', 'page_responses', 1322 $a->id, $options); 1323 } 1324 $answers[] = $answer; 1325 } 1326 1327 // Additional lesson information. 1328 if (!$lesson->can_manage()) { 1329 if ($lesson->ongoing && !$reviewmode) { 1330 $ongoingscore = $lesson->get_ongoing_score_message(); 1331 } 1332 if ($lesson->progressbar) { 1333 $progress = $lesson->calculate_progress(); 1334 } 1335 } 1336 } 1337 } 1338 1339 $messages = self::format_lesson_messages($lesson); 1340 1341 $result = array( 1342 'newpageid' => $pageid, 1343 'ongoingscore' => $ongoingscore, 1344 'progress' => $progress, 1345 'contentfiles' => $contentfiles, 1346 'answers' => $answers, 1347 'messages' => $messages, 1348 'warnings' => $warnings, 1349 'displaymenu' => !empty(lesson_displayleftif($lesson)), 1350 ); 1351 1352 if (!empty($pagedata)) { 1353 $result['page'] = $pagedata; 1354 } 1355 if ($params['returncontents']) { 1356 $result['pagecontent'] = $pagecontent; // Return the complete page contents rendered. 1357 } 1358 1359 return $result; 1360 } 1361 1362 /** 1363 * Describes the get_page_data return value. 1364 * 1365 * @return external_single_structure 1366 * @since Moodle 3.3 1367 */ 1368 public static function get_page_data_returns() { 1369 return new external_single_structure( 1370 array( 1371 'page' => self::get_page_structure(VALUE_OPTIONAL), 1372 'newpageid' => new external_value(PARAM_INT, 'New page id (if a jump was made)'), 1373 'pagecontent' => new external_value(PARAM_RAW, 'Page html content', VALUE_OPTIONAL), 1374 'ongoingscore' => new external_value(PARAM_TEXT, 'The ongoing score message'), 1375 'progress' => new external_value(PARAM_INT, 'Progress percentage in the lesson'), 1376 'contentfiles' => new external_files(), 1377 'answers' => new external_multiple_structure( 1378 new external_single_structure( 1379 array( 1380 'id' => new external_value(PARAM_INT, 'The ID of this answer in the database'), 1381 'answerfiles' => new external_files(), 1382 'responsefiles' => new external_files(), 1383 'jumpto' => new external_value(PARAM_INT, 'Identifies where the user goes upon completing a page with this answer', 1384 VALUE_OPTIONAL), 1385 'grade' => new external_value(PARAM_INT, 'The grade this answer is worth', VALUE_OPTIONAL), 1386 'score' => new external_value(PARAM_INT, 'The score this answer will give', VALUE_OPTIONAL), 1387 'flags' => new external_value(PARAM_INT, 'Used to store options for the answer', VALUE_OPTIONAL), 1388 'timecreated' => new external_value(PARAM_INT, 'A timestamp of when the answer was created', VALUE_OPTIONAL), 1389 'timemodified' => new external_value(PARAM_INT, 'A timestamp of when the answer was modified', VALUE_OPTIONAL), 1390 'answer' => new external_value(PARAM_RAW, 'Possible answer text', VALUE_OPTIONAL), 1391 'answerformat' => new external_format_value('answer', VALUE_OPTIONAL), 1392 'response' => new external_value(PARAM_RAW, 'Response text for the answer', VALUE_OPTIONAL), 1393 'responseformat' => new external_format_value('response', VALUE_OPTIONAL), 1394 ), 'The page answers' 1395 1396 ) 1397 ), 1398 'messages' => self::external_messages(), 1399 'displaymenu' => new external_value(PARAM_BOOL, 'Whether we should display the menu or not in this page.'), 1400 'warnings' => new external_warnings(), 1401 ) 1402 ); 1403 } 1404 1405 /** 1406 * Describes the parameters for process_page. 1407 * 1408 * @return external_function_parameters 1409 * @since Moodle 3.3 1410 */ 1411 public static function process_page_parameters() { 1412 return new external_function_parameters ( 1413 array( 1414 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 1415 'pageid' => new external_value(PARAM_INT, 'the page id'), 1416 'data' => new external_multiple_structure( 1417 new external_single_structure( 1418 array( 1419 'name' => new external_value(PARAM_RAW, 'data name'), 1420 'value' => new external_value(PARAM_RAW, 'data value'), 1421 ) 1422 ), 'the data to be saved' 1423 ), 1424 'password' => new external_value(PARAM_RAW, 'optional password (the lesson may be protected)', VALUE_DEFAULT, ''), 1425 'review' => new external_value(PARAM_BOOL, 'if we want to review just after finishing (1 hour margin)', 1426 VALUE_DEFAULT, false), 1427 ) 1428 ); 1429 } 1430 1431 /** 1432 * Processes page responses 1433 * 1434 * @param int $lessonid lesson instance id 1435 * @param int $pageid page id 1436 * @param array $data the data to be saved 1437 * @param string $password optional password (the lesson may be protected) 1438 * @param bool $review if we want to review just after finishing (1 hour margin) 1439 * @return array of warnings and status result 1440 * @since Moodle 3.3 1441 * @throws moodle_exception 1442 */ 1443 public static function process_page($lessonid, $pageid, $data, $password = '', $review = false) { 1444 global $USER; 1445 1446 $params = array('lessonid' => $lessonid, 'pageid' => $pageid, 'data' => $data, 'password' => $password, 1447 'review' => $review); 1448 $params = self::validate_parameters(self::process_page_parameters(), $params); 1449 1450 $warnings = array(); 1451 $pagecontent = $ongoingscore = ''; 1452 $progress = null; 1453 1454 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 1455 1456 // Update timer so the validation can check the time restrictions. 1457 $timer = $lesson->update_timer(); 1458 self::validate_attempt($lesson, $params); 1459 1460 // Create the $_POST object required by the lesson question engine. 1461 $_POST = array(); 1462 foreach ($data as $element) { 1463 // First check if we are handling editor fields like answer[text]. 1464 if (preg_match('/(.+)\[(.+)\]$/', $element['name'], $matches)) { 1465 $_POST[$matches[1]][$matches[2]] = $element['value']; 1466 } else { 1467 $_POST[$element['name']] = $element['value']; 1468 } 1469 } 1470 1471 // Ignore sesskey (deep in some APIs), the request is already validated. 1472 $USER->ignoresesskey = true; 1473 1474 // Process page. 1475 $page = $lesson->load_page($params['pageid']); 1476 $result = $lesson->process_page_responses($page); 1477 1478 // Prepare messages. 1479 $reviewmode = $lesson->is_in_review_mode(); 1480 $lesson->add_messages_on_page_process($page, $result, $reviewmode); 1481 1482 // Additional lesson information. 1483 if (!$lesson->can_manage()) { 1484 if ($lesson->ongoing && !$reviewmode) { 1485 $ongoingscore = $lesson->get_ongoing_score_message(); 1486 } 1487 if ($lesson->progressbar) { 1488 $progress = $lesson->calculate_progress(); 1489 } 1490 } 1491 1492 // Check conditionally everything coming from result (except newpageid because is always set). 1493 $result = array( 1494 'newpageid' => (int) $result->newpageid, 1495 'inmediatejump' => $result->inmediatejump, 1496 'nodefaultresponse' => !empty($result->nodefaultresponse), 1497 'feedback' => (isset($result->feedback)) ? $result->feedback : '', 1498 'attemptsremaining' => (isset($result->attemptsremaining)) ? $result->attemptsremaining : null, 1499 'correctanswer' => !empty($result->correctanswer), 1500 'noanswer' => !empty($result->noanswer), 1501 'isessayquestion' => !empty($result->isessayquestion), 1502 'maxattemptsreached' => !empty($result->maxattemptsreached), 1503 'response' => (isset($result->response)) ? $result->response : '', 1504 'studentanswer' => (isset($result->studentanswer)) ? $result->studentanswer : '', 1505 'userresponse' => (isset($result->userresponse)) ? $result->userresponse : '', 1506 'reviewmode' => $reviewmode, 1507 'ongoingscore' => $ongoingscore, 1508 'progress' => $progress, 1509 'displaymenu' => !empty(lesson_displayleftif($lesson)), 1510 'messages' => self::format_lesson_messages($lesson), 1511 'warnings' => $warnings, 1512 ); 1513 return $result; 1514 } 1515 1516 /** 1517 * Describes the process_page return value. 1518 * 1519 * @return external_single_structure 1520 * @since Moodle 3.3 1521 */ 1522 public static function process_page_returns() { 1523 return new external_single_structure( 1524 array( 1525 'newpageid' => new external_value(PARAM_INT, 'New page id (if a jump was made).'), 1526 'inmediatejump' => new external_value(PARAM_BOOL, 'Whether the page processing redirect directly to anoter page.'), 1527 'nodefaultresponse' => new external_value(PARAM_BOOL, 'Whether there is not a default response.'), 1528 'feedback' => new external_value(PARAM_RAW, 'The response feedback.'), 1529 'attemptsremaining' => new external_value(PARAM_INT, 'Number of attempts remaining.'), 1530 'correctanswer' => new external_value(PARAM_BOOL, 'Whether the answer is correct.'), 1531 'noanswer' => new external_value(PARAM_BOOL, 'Whether there aren\'t answers.'), 1532 'isessayquestion' => new external_value(PARAM_BOOL, 'Whether is a essay question.'), 1533 'maxattemptsreached' => new external_value(PARAM_BOOL, 'Whether we reachered the max number of attempts.'), 1534 'response' => new external_value(PARAM_RAW, 'The response.'), 1535 'studentanswer' => new external_value(PARAM_RAW, 'The student answer.'), 1536 'userresponse' => new external_value(PARAM_RAW, 'The user response.'), 1537 'reviewmode' => new external_value(PARAM_BOOL, 'Whether the user is reviewing.'), 1538 'ongoingscore' => new external_value(PARAM_TEXT, 'The ongoing message.'), 1539 'progress' => new external_value(PARAM_INT, 'Progress percentage in the lesson.'), 1540 'displaymenu' => new external_value(PARAM_BOOL, 'Whether we should display the menu or not in this page.'), 1541 'messages' => self::external_messages(), 1542 'warnings' => new external_warnings(), 1543 ) 1544 ); 1545 } 1546 1547 /** 1548 * Describes the parameters for finish_attempt. 1549 * 1550 * @return external_function_parameters 1551 * @since Moodle 3.3 1552 */ 1553 public static function finish_attempt_parameters() { 1554 return new external_function_parameters ( 1555 array( 1556 'lessonid' => new external_value(PARAM_INT, 'Lesson instance id.'), 1557 'password' => new external_value(PARAM_RAW, 'Optional password (the lesson may be protected).', VALUE_DEFAULT, ''), 1558 'outoftime' => new external_value(PARAM_BOOL, 'If the user run out of time.', VALUE_DEFAULT, false), 1559 'review' => new external_value(PARAM_BOOL, 'If we want to review just after finishing (1 hour margin).', 1560 VALUE_DEFAULT, false), 1561 ) 1562 ); 1563 } 1564 1565 /** 1566 * Finishes the current attempt. 1567 * 1568 * @param int $lessonid lesson instance id 1569 * @param string $password optional password (the lesson may be protected) 1570 * @param bool $outoftime optional if the user run out of time 1571 * @param bool $review if we want to review just after finishing (1 hour margin) 1572 * @return array of warnings and information about the finished attempt 1573 * @since Moodle 3.3 1574 * @throws moodle_exception 1575 */ 1576 public static function finish_attempt($lessonid, $password = '', $outoftime = false, $review = false) { 1577 1578 $params = array('lessonid' => $lessonid, 'password' => $password, 'outoftime' => $outoftime, 'review' => $review); 1579 $params = self::validate_parameters(self::finish_attempt_parameters(), $params); 1580 1581 $warnings = array(); 1582 1583 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 1584 1585 // Update timer so the validation can check the time restrictions. 1586 $timer = $lesson->update_timer(); 1587 1588 // Return the validation to avoid exceptions in case the user is out of time. 1589 $params['pageid'] = LESSON_EOL; 1590 $validation = self::validate_attempt($lesson, $params, true); 1591 1592 if (array_key_exists('eolstudentoutoftime', $validation)) { 1593 // Maybe we run out of time just now. 1594 $params['outoftime'] = true; 1595 unset($validation['eolstudentoutoftime']); 1596 } 1597 // Check if there are more errors. 1598 if (!empty($validation)) { 1599 reset($validation); 1600 throw new moodle_exception(key($validation), 'lesson', '', current($validation)); // Throw first error. 1601 } 1602 1603 // Set out of time to normal (it is the only existing mode). 1604 $outoftimemode = $params['outoftime'] ? 'normal' : ''; 1605 $result = $lesson->process_eol_page($outoftimemode); 1606 1607 // Return the data. 1608 $validmessages = array( 1609 'notenoughtimespent', 'numberofpagesviewed', 'youshouldview', 'numberofcorrectanswers', 1610 'displayscorewithessays', 'displayscorewithoutessays', 'yourcurrentgradeisoutof', 'eolstudentoutoftimenoanswers', 1611 'welldone', 'displayofgrade', 'modattemptsnoteacher', 'progresscompleted'); 1612 1613 $data = array(); 1614 foreach ($result as $el => $value) { 1615 if ($value !== false) { 1616 $message = ''; 1617 if (in_array($el, $validmessages)) { // Check if the data comes with an informative message. 1618 $a = (is_bool($value)) ? null : $value; 1619 $message = get_string($el, 'lesson', $a); 1620 } 1621 // Return the data. 1622 $data[] = array( 1623 'name' => $el, 1624 'value' => (is_bool($value)) ? 1 : json_encode($value), // The data can be a php object. 1625 'message' => $message 1626 ); 1627 } 1628 } 1629 1630 $result = array( 1631 'data' => $data, 1632 'messages' => self::format_lesson_messages($lesson), 1633 'warnings' => $warnings, 1634 ); 1635 return $result; 1636 } 1637 1638 /** 1639 * Describes the finish_attempt return value. 1640 * 1641 * @return external_single_structure 1642 * @since Moodle 3.3 1643 */ 1644 public static function finish_attempt_returns() { 1645 return new external_single_structure( 1646 array( 1647 'data' => new external_multiple_structure( 1648 new external_single_structure( 1649 array( 1650 'name' => new external_value(PARAM_ALPHANUMEXT, 'Data name.'), 1651 'value' => new external_value(PARAM_RAW, 'Data value.'), 1652 'message' => new external_value(PARAM_RAW, 'Data message (translated string).'), 1653 ) 1654 ), 'The EOL page information data.' 1655 ), 1656 'messages' => self::external_messages(), 1657 'warnings' => new external_warnings(), 1658 ) 1659 ); 1660 } 1661 1662 /** 1663 * Describes the parameters for get_attempts_overview. 1664 * 1665 * @return external_function_parameters 1666 * @since Moodle 3.3 1667 */ 1668 public static function get_attempts_overview_parameters() { 1669 return new external_function_parameters ( 1670 array( 1671 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 1672 'groupid' => new external_value(PARAM_INT, 'group id, 0 means that the function will determine the user group', 1673 VALUE_DEFAULT, 0), 1674 ) 1675 ); 1676 } 1677 1678 /** 1679 * Get a list of all the attempts made by users in a lesson. 1680 * 1681 * @param int $lessonid lesson instance id 1682 * @param int $groupid group id, 0 means that the function will determine the user group 1683 * @return array of warnings and status result 1684 * @since Moodle 3.3 1685 * @throws moodle_exception 1686 */ 1687 public static function get_attempts_overview($lessonid, $groupid = 0) { 1688 1689 $params = array('lessonid' => $lessonid, 'groupid' => $groupid); 1690 $params = self::validate_parameters(self::get_attempts_overview_parameters(), $params); 1691 $warnings = array(); 1692 1693 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 1694 require_capability('mod/lesson:viewreports', $context); 1695 1696 if (!empty($params['groupid'])) { 1697 $groupid = $params['groupid']; 1698 // Determine is the group is visible to user. 1699 if (!groups_group_visible($groupid, $course, $cm)) { 1700 throw new moodle_exception('notingroup'); 1701 } 1702 } else { 1703 // Check to see if groups are being used here. 1704 if ($groupmode = groups_get_activity_groupmode($cm)) { 1705 $groupid = groups_get_activity_group($cm); 1706 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups). 1707 if (!groups_group_visible($groupid, $course, $cm)) { 1708 throw new moodle_exception('notingroup'); 1709 } 1710 } else { 1711 $groupid = 0; 1712 } 1713 } 1714 1715 $result = array( 1716 'warnings' => $warnings 1717 ); 1718 1719 list($table, $data) = lesson_get_overview_report_table_and_data($lesson, $groupid); 1720 if ($data !== false) { 1721 $result['data'] = $data; 1722 } 1723 1724 return $result; 1725 } 1726 1727 /** 1728 * Describes the get_attempts_overview return value. 1729 * 1730 * @return external_single_structure 1731 * @since Moodle 3.3 1732 */ 1733 public static function get_attempts_overview_returns() { 1734 return new external_single_structure( 1735 array( 1736 'data' => new external_single_structure( 1737 array( 1738 'lessonscored' => new external_value(PARAM_BOOL, 'True if the lesson was scored.'), 1739 'numofattempts' => new external_value(PARAM_INT, 'Number of attempts.'), 1740 'avescore' => new external_value(PARAM_FLOAT, 'Average score.'), 1741 'highscore' => new external_value(PARAM_FLOAT, 'High score.'), 1742 'lowscore' => new external_value(PARAM_FLOAT, 'Low score.'), 1743 'avetime' => new external_value(PARAM_INT, 'Average time (spent in taking the lesson).'), 1744 'hightime' => new external_value(PARAM_INT, 'High time.'), 1745 'lowtime' => new external_value(PARAM_INT, 'Low time.'), 1746 'students' => new external_multiple_structure( 1747 new external_single_structure( 1748 array( 1749 'id' => new external_value(PARAM_INT, 'User id.'), 1750 'fullname' => new external_value(PARAM_TEXT, 'User full name.'), 1751 'bestgrade' => new external_value(PARAM_FLOAT, 'Best grade.'), 1752 'attempts' => new external_multiple_structure( 1753 new external_single_structure( 1754 array( 1755 'try' => new external_value(PARAM_INT, 'Attempt number.'), 1756 'grade' => new external_value(PARAM_FLOAT, 'Attempt grade.'), 1757 'timestart' => new external_value(PARAM_INT, 'Attempt time started.'), 1758 'timeend' => new external_value(PARAM_INT, 'Attempt last time continued.'), 1759 'end' => new external_value(PARAM_INT, 'Attempt time ended.'), 1760 ) 1761 ) 1762 ) 1763 ) 1764 ), 'Students data, including attempts.', VALUE_OPTIONAL 1765 ), 1766 ), 1767 'Attempts overview data (empty for no attemps).', VALUE_OPTIONAL 1768 ), 1769 'warnings' => new external_warnings(), 1770 ) 1771 ); 1772 } 1773 1774 /** 1775 * Describes the parameters for get_user_attempt. 1776 * 1777 * @return external_function_parameters 1778 * @since Moodle 3.3 1779 */ 1780 public static function get_user_attempt_parameters() { 1781 return new external_function_parameters ( 1782 array( 1783 'lessonid' => new external_value(PARAM_INT, 'Lesson instance id.'), 1784 'userid' => new external_value(PARAM_INT, 'The user id. 0 for current user.'), 1785 'lessonattempt' => new external_value(PARAM_INT, 'The attempt number.'), 1786 ) 1787 ); 1788 } 1789 1790 /** 1791 * Return information about the given user attempt (including answers). 1792 * 1793 * @param int $lessonid lesson instance id 1794 * @param int $userid the user id 1795 * @param int $lessonattempt the attempt number 1796 * @return array of warnings and page attempts 1797 * @since Moodle 3.3 1798 * @throws moodle_exception 1799 */ 1800 public static function get_user_attempt($lessonid, $userid, $lessonattempt) { 1801 global $USER; 1802 1803 $params = array( 1804 'lessonid' => $lessonid, 1805 'userid' => $userid, 1806 'lessonattempt' => $lessonattempt, 1807 ); 1808 $params = self::validate_parameters(self::get_user_attempt_parameters(), $params); 1809 $warnings = array(); 1810 1811 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 1812 1813 // Default value for userid. 1814 if (empty($params['userid'])) { 1815 $params['userid'] = $USER->id; 1816 } 1817 1818 // Extra checks so only users with permissions can view other users attempts. 1819 if ($USER->id != $params['userid']) { 1820 self::check_can_view_user_data($params['userid'], $course, $cm, $context); 1821 } 1822 1823 list($answerpages, $userstats) = lesson_get_user_detailed_report_data($lesson, $userid, $params['lessonattempt']); 1824 // Convert page object to page record. 1825 foreach ($answerpages as $answerp) { 1826 $answerp->page = self::get_page_fields($answerp->page); 1827 } 1828 1829 $result = array( 1830 'answerpages' => $answerpages, 1831 'userstats' => $userstats, 1832 'warnings' => $warnings, 1833 ); 1834 return $result; 1835 } 1836 1837 /** 1838 * Describes the get_user_attempt return value. 1839 * 1840 * @return external_single_structure 1841 * @since Moodle 3.3 1842 */ 1843 public static function get_user_attempt_returns() { 1844 return new external_single_structure( 1845 array( 1846 'answerpages' => new external_multiple_structure( 1847 new external_single_structure( 1848 array( 1849 'page' => self::get_page_structure(VALUE_OPTIONAL), 1850 'title' => new external_value(PARAM_RAW, 'Page title.'), 1851 'contents' => new external_value(PARAM_RAW, 'Page contents.'), 1852 'qtype' => new external_value(PARAM_TEXT, 'Identifies the page type of this page.'), 1853 'grayout' => new external_value(PARAM_INT, 'If is required to apply a grayout.'), 1854 'answerdata' => new external_single_structure( 1855 array( 1856 'score' => new external_value(PARAM_TEXT, 'The score (text version).'), 1857 'response' => new external_value(PARAM_RAW, 'The response text.'), 1858 'responseformat' => new external_format_value('response.'), 1859 'answers' => new external_multiple_structure( 1860 new external_multiple_structure(new external_value(PARAM_RAW, 'Possible answers and info.')), 1861 'User answers', 1862 VALUE_OPTIONAL 1863 ), 1864 ), 'Answer data (empty in content pages created in Moodle 1.x).', VALUE_OPTIONAL 1865 ) 1866 ) 1867 ) 1868 ), 1869 'userstats' => new external_single_structure( 1870 array( 1871 'grade' => new external_value(PARAM_FLOAT, 'Attempt final grade.'), 1872 'completed' => new external_value(PARAM_INT, 'Time completed.'), 1873 'timetotake' => new external_value(PARAM_INT, 'Time taken.'), 1874 'gradeinfo' => self::get_user_attempt_grade_structure(VALUE_OPTIONAL) 1875 ) 1876 ), 1877 'warnings' => new external_warnings(), 1878 ) 1879 ); 1880 } 1881 1882 /** 1883 * Describes the parameters for get_pages_possible_jumps. 1884 * 1885 * @return external_function_parameters 1886 * @since Moodle 3.3 1887 */ 1888 public static function get_pages_possible_jumps_parameters() { 1889 return new external_function_parameters ( 1890 array( 1891 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 1892 ) 1893 ); 1894 } 1895 1896 /** 1897 * Return all the possible jumps for the pages in a given lesson. 1898 * 1899 * You may expect different results on consecutive executions due to the random nature of the lesson module. 1900 * 1901 * @param int $lessonid lesson instance id 1902 * @return array of warnings and possible jumps 1903 * @since Moodle 3.3 1904 * @throws moodle_exception 1905 */ 1906 public static function get_pages_possible_jumps($lessonid) { 1907 global $USER; 1908 1909 $params = array('lessonid' => $lessonid); 1910 $params = self::validate_parameters(self::get_pages_possible_jumps_parameters(), $params); 1911 1912 $warnings = $jumps = array(); 1913 1914 list($lesson, $course, $cm, $context) = self::validate_lesson($params['lessonid']); 1915 1916 // Only return for managers or if offline attempts are enabled. 1917 if ($lesson->can_manage() || $lesson->allowofflineattempts) { 1918 1919 $lessonpages = $lesson->load_all_pages(); 1920 foreach ($lessonpages as $page) { 1921 $jump = array(); 1922 $jump['pageid'] = $page->id; 1923 1924 $answers = $page->get_answers(); 1925 if (count($answers) > 0) { 1926 foreach ($answers as $answer) { 1927 $jump['answerid'] = $answer->id; 1928 $jump['jumpto'] = $answer->jumpto; 1929 $jump['calculatedjump'] = $lesson->calculate_new_page_on_jump($page, $answer->jumpto); 1930 // Special case, only applies to branch/end of branch. 1931 if ($jump['calculatedjump'] == LESSON_RANDOMBRANCH) { 1932 $jump['calculatedjump'] = lesson_unseen_branch_jump($lesson, $USER->id); 1933 } 1934 $jumps[] = $jump; 1935 } 1936 } else { 1937 // Imported lessons from 1.x. 1938 $jump['answerid'] = 0; 1939 $jump['jumpto'] = $page->nextpageid; 1940 $jump['calculatedjump'] = $lesson->calculate_new_page_on_jump($page, $page->nextpageid); 1941 $jumps[] = $jump; 1942 } 1943 } 1944 } 1945 1946 $result = array( 1947 'jumps' => $jumps, 1948 'warnings' => $warnings, 1949 ); 1950 return $result; 1951 } 1952 1953 /** 1954 * Describes the get_pages_possible_jumps return value. 1955 * 1956 * @return external_single_structure 1957 * @since Moodle 3.3 1958 */ 1959 public static function get_pages_possible_jumps_returns() { 1960 return new external_single_structure( 1961 array( 1962 'jumps' => new external_multiple_structure( 1963 new external_single_structure( 1964 array( 1965 'pageid' => new external_value(PARAM_INT, 'The page id'), 1966 'answerid' => new external_value(PARAM_INT, 'The answer id'), 1967 'jumpto' => new external_value(PARAM_INT, 'The jump (page id or type of jump)'), 1968 'calculatedjump' => new external_value(PARAM_INT, 'The real page id (or EOL) to jump'), 1969 ), 'Jump for a page answer' 1970 ) 1971 ), 1972 'warnings' => new external_warnings(), 1973 ) 1974 ); 1975 } 1976 1977 /** 1978 * Describes the parameters for get_lesson. 1979 * 1980 * @return external_function_parameters 1981 * @since Moodle 3.3 1982 */ 1983 public static function get_lesson_parameters() { 1984 return new external_function_parameters ( 1985 array( 1986 'lessonid' => new external_value(PARAM_INT, 'lesson instance id'), 1987 'password' => new external_value(PARAM_RAW, 'lesson password', VALUE_DEFAULT, ''), 1988 ) 1989 ); 1990 } 1991 1992 /** 1993 * Return information of a given lesson. 1994 * 1995 * @param int $lessonid lesson instance id 1996 * @param string $password optional password (the lesson may be protected) 1997 * @return array of warnings and status result 1998 * @since Moodle 3.3 1999 * @throws moodle_exception 2000 */ 2001 public static function get_lesson($lessonid, $password = '') { 2002 global $PAGE; 2003 2004 $params = array('lessonid' => $lessonid, 'password' => $password); 2005 $params = self::validate_parameters(self::get_lesson_parameters(), $params); 2006 $warnings = array(); 2007 2008 list($lesson, $course, $cm, $context, $lessonrecord) = self::validate_lesson($params['lessonid']); 2009 2010 $lessonrecord = self::get_lesson_summary_for_exporter($lessonrecord, $params['password']); 2011 $exporter = new lesson_summary_exporter($lessonrecord, array('context' => $context)); 2012 2013 $result = array(); 2014 $result['lesson'] = $exporter->export($PAGE->get_renderer('core')); 2015 $result['warnings'] = $warnings; 2016 return $result; 2017 } 2018 2019 /** 2020 * Describes the get_lesson return value. 2021 * 2022 * @return external_single_structure 2023 * @since Moodle 3.3 2024 */ 2025 public static function get_lesson_returns() { 2026 return new external_single_structure( 2027 array( 2028 'lesson' => lesson_summary_exporter::get_read_structure(), 2029 'warnings' => new external_warnings(), 2030 ) 2031 ); 2032 } 2033 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body