2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
22 * @copyright 2017 Juan Leyva <juan@moodle.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') ||
die;
29 require_once($CFG->libdir
. '/externallib.php');
30 require_once($CFG->dirroot
. '/mod/lesson/locallib.php');
33 * Lesson external functions
37 * @copyright 2017 Juan Leyva <juan@moodle.com>
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41 class mod_lesson_external
extends external_api
{
44 * Describes the parameters for get_lessons_by_courses.
46 * @return external_function_parameters
49 public static function get_lessons_by_courses_parameters() {
50 return new external_function_parameters (
52 'courseids' => new external_multiple_structure(
53 new external_value(PARAM_INT
, 'course id'), 'Array of course ids', VALUE_DEFAULT
, array()
60 * Returns a list of lessons in a provided list of courses,
61 * if no list is provided all lessons that the user can view will be returned.
63 * @param array $courseids Array of course ids
64 * @return array of lessons details
67 public static function get_lessons_by_courses($courseids = array()) {
71 $returnedlessons = array();
74 'courseids' => $courseids,
76 $params = self
::validate_parameters(self
::get_lessons_by_courses_parameters(), $params);
79 if (empty($params['courseids'])) {
80 $mycourses = enrol_get_my_courses();
81 $params['courseids'] = array_keys($mycourses);
84 // Ensure there are courseids to loop through.
85 if (!empty($params['courseids'])) {
87 list($courses, $warnings) = external_util
::validate_courses($params['courseids'], $mycourses);
89 // Get the lessons in this course, this function checks users visibility permissions.
90 // We can avoid then additional validate_context calls.
91 $lessons = get_all_instances_in_courses("lesson", $courses);
92 foreach ($lessons as $lesson) {
93 $context = context_module
::instance($lesson->coursemodule
);
95 $lesson = new lesson($lesson);
96 $lesson->update_effective_access($USER->id
);
99 $lessondetails = array();
100 // First, we return information that any user can see in the web interface.
101 $lessondetails['id'] = $lesson->id
;
102 $lessondetails['coursemodule'] = $lesson->coursemodule
;
103 $lessondetails['course'] = $lesson->course
;
104 $lessondetails['name'] = external_format_string($lesson->name
, $context->id
);
106 $lessonavailable = $lesson->get_time_restriction_status() === false;
107 $lessonavailable = $lessonavailable && $lesson->get_password_restriction_status('') === false;
108 $lessonavailable = $lessonavailable && $lesson->get_dependencies_restriction_status() === false;
110 if ($lessonavailable) {
112 list($lessondetails['intro'], $lessondetails['introformat']) = external_format_text($lesson->intro
,
113 $lesson->introformat
, $context->id
, 'mod_lesson', 'intro', null);
115 $lessondetails['introfiles'] = external_util
::get_area_files($context->id
, 'mod_lesson', 'intro', false, false);
116 $lessondetails['mediafiles'] = external_util
::get_area_files($context->id
, 'mod_lesson', 'mediafile', 0);
117 $viewablefields = array('practice', 'modattempts', 'usepassword', 'grade', 'custom', 'ongoing', 'usemaxgrade',
118 'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback', 'minquestions',
119 'maxpages', 'timelimit', 'retake', 'mediafile', 'mediaheight', 'mediawidth',
120 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif',
123 // Fields only for managers.
124 if ($lesson->can_manage()) {
125 $additionalfields = array('password', 'dependency', 'conditions', 'activitylink', 'available', 'deadline',
126 'timemodified', 'completionendreached', 'completiontimespent');
127 $viewablefields = array_merge($viewablefields, $additionalfields);
130 foreach ($viewablefields as $field) {
131 $lessondetails[$field] = $lesson->{$field};
134 $returnedlessons[] = $lessondetails;
138 $result['lessons'] = $returnedlessons;
139 $result['warnings'] = $warnings;
144 * Describes the get_lessons_by_courses return value.
146 * @return external_single_structure
149 public static function get_lessons_by_courses_returns() {
150 return new external_single_structure(
152 'lessons' => new external_multiple_structure(
153 new external_single_structure(
155 'id' => new external_value(PARAM_INT
, 'Standard Moodle primary key.'),
156 'course' => new external_value(PARAM_INT
, 'Foreign key reference to the course this lesson is part of.'),
157 'coursemodule' => new external_value(PARAM_INT
, 'Course module id.'),
158 'name' => new external_value(PARAM_RAW
, 'Lesson name.'),
159 'intro' => new external_value(PARAM_RAW
, 'Lesson introduction text.', VALUE_OPTIONAL
),
160 'introformat' => new external_format_value('intro', VALUE_OPTIONAL
),
161 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL
),
162 'practice' => new external_value(PARAM_INT
, 'Practice lesson?', VALUE_OPTIONAL
),
163 'modattempts' => new external_value(PARAM_INT
, 'Allow student review?', VALUE_OPTIONAL
),
164 'usepassword' => new external_value(PARAM_INT
, 'Password protected lesson?', VALUE_OPTIONAL
),
165 'password' => new external_value(PARAM_RAW
, 'Password', VALUE_OPTIONAL
),
166 'dependency' => new external_value(PARAM_INT
, 'Dependent on (another lesson id)', VALUE_OPTIONAL
),
167 'conditions' => new external_value(PARAM_RAW
, 'Conditions to enable the lesson', VALUE_OPTIONAL
),
168 'grade' => new external_value(PARAM_INT
, 'The total that the grade is scaled to be out of',
170 'custom' => new external_value(PARAM_INT
, 'Custom scoring?', VALUE_OPTIONAL
),
171 'ongoing' => new external_value(PARAM_INT
, 'Display ongoing score?', VALUE_OPTIONAL
),
172 'usemaxgrade' => new external_value(PARAM_INT
, 'How to calculate the final grade', VALUE_OPTIONAL
),
173 'maxanswers' => new external_value(PARAM_INT
, 'Maximum answers per page', VALUE_OPTIONAL
),
174 'maxattempts' => new external_value(PARAM_INT
, 'Maximum attempts', VALUE_OPTIONAL
),
175 'review' => new external_value(PARAM_INT
, 'Provide option to try a question again', VALUE_OPTIONAL
),
176 'nextpagedefault' => new external_value(PARAM_INT
, 'Action for a correct answer', VALUE_OPTIONAL
),
177 'feedback' => new external_value(PARAM_INT
, 'Display default feedback', VALUE_OPTIONAL
),
178 'minquestions' => new external_value(PARAM_INT
, 'Minimum number of questions', VALUE_OPTIONAL
),
179 'maxpages' => new external_value(PARAM_INT
, 'Number of pages to show', VALUE_OPTIONAL
),
180 'timelimit' => new external_value(PARAM_INT
, 'Time limit', VALUE_OPTIONAL
),
181 'retake' => new external_value(PARAM_INT
, 'Re-takes allowed', VALUE_OPTIONAL
),
182 'activitylink' => new external_value(PARAM_INT
, 'Link to next activity', VALUE_OPTIONAL
),
183 'mediafile' => new external_value(PARAM_RAW
, 'Local file path or full external URL', VALUE_OPTIONAL
),
184 'mediafiles' => new external_files('Media files', VALUE_OPTIONAL
),
185 'mediaheight' => new external_value(PARAM_INT
, 'Popup for media file height', VALUE_OPTIONAL
),
186 'mediawidth' => new external_value(PARAM_INT
, 'Popup for media with', VALUE_OPTIONAL
),
187 'mediaclose' => new external_value(PARAM_INT
, 'Display a close button in the popup?', VALUE_OPTIONAL
),
188 'slideshow' => new external_value(PARAM_INT
, 'Display lesson as slideshow', VALUE_OPTIONAL
),
189 'width' => new external_value(PARAM_INT
, 'Slideshow width', VALUE_OPTIONAL
),
190 'height' => new external_value(PARAM_INT
, 'Slideshow height', VALUE_OPTIONAL
),
191 'bgcolor' => new external_value(PARAM_TEXT
, 'Slideshow bgcolor', VALUE_OPTIONAL
),
192 'displayleft' => new external_value(PARAM_INT
, 'Display left pages menu?', VALUE_OPTIONAL
),
193 'displayleftif' => new external_value(PARAM_INT
, 'Minimum grade to display menu', VALUE_OPTIONAL
),
194 'progressbar' => new external_value(PARAM_INT
, 'Display progress bar?', VALUE_OPTIONAL
),
195 'available' => new external_value(PARAM_INT
, 'Available from', VALUE_OPTIONAL
),
196 'deadline' => new external_value(PARAM_INT
, 'Available until', VALUE_OPTIONAL
),
197 'timemodified' => new external_value(PARAM_INT
, 'Last time settings were updated', VALUE_OPTIONAL
),
198 'completionendreached' => new external_value(PARAM_INT
, 'Require end reached for completion?',
200 'completiontimespent' => new external_value(PARAM_INT
, 'Student must do this activity at least for',
202 'visible' => new external_value(PARAM_INT
, 'Visible?', VALUE_OPTIONAL
),
203 'groupmode' => new external_value(PARAM_INT
, 'Group mode', VALUE_OPTIONAL
),
204 'groupingid' => new external_value(PARAM_INT
, 'Grouping id', VALUE_OPTIONAL
),
208 'warnings' => new external_warnings(),
214 * Utility function for validating a lesson.
216 * @param int $lessonid lesson instance id
217 * @return array array containing the lesson, course, context and course module objects
220 protected static function validate_lesson($lessonid) {
223 // Request and permission validation.
224 $lesson = $DB->get_record('lesson', array('id' => $lessonid), '*', MUST_EXIST
);
225 list($course, $cm) = get_course_and_cm_from_instance($lesson, 'lesson');
227 $lesson = new lesson($lesson, $cm, $course);
228 $lesson->update_effective_access($USER->id
);
230 $context = $lesson->context
;
231 self
::validate_context($context);
233 return array($lesson, $course, $cm, $context);
237 * Validates a new attempt.
239 * @param lesson $lesson lesson instance
240 * @param array $params request parameters
241 * @param boolean $return whether to return the errors or throw exceptions
242 * @return array the errors (if return set to true)
245 protected static function validate_attempt(lesson
$lesson, $params, $return = false) {
250 // Avoid checkings for managers.
251 if ($lesson->can_manage()) {
256 if ($timerestriction = $lesson->get_time_restriction_status()) {
257 $error = ["$timerestriction->reason" => userdate($timerestriction->time
)];
259 throw new moodle_exception(key($error), 'lesson', '', current($error));
261 $errors[key($error)] = current($error);
264 // Password protected lesson code.
265 if ($passwordrestriction = $lesson->get_password_restriction_status($params['password'])) {
266 $error = ["passwordprotectedlesson" => external_format_string($lesson->name
, $lesson->context
->id
)];
268 throw new moodle_exception(key($error), 'lesson', '', current($error));
270 $errors[key($error)] = current($error);
273 // Check for dependencies.
274 if ($dependenciesrestriction = $lesson->get_dependencies_restriction_status()) {
275 $errorhtmllist = implode(get_string('and', 'lesson') . ', ', $dependenciesrestriction->errors
);
276 $error = ["completethefollowingconditions" => $dependenciesrestriction->dependentlesson
->name
. $errorhtmllist];
278 throw new moodle_exception(key($error), 'lesson', '', current($error));
280 $errors[key($error)] = current($error);
283 // To check only when no page is set (starting or continuing a lesson).
284 if (empty($params['pageid'])) {
285 // To avoid multiple calls, store the magic property firstpage.
286 $lessonfirstpage = $lesson->firstpage
;
287 $lessonfirstpageid = $lessonfirstpage ?
$lessonfirstpage->id
: false;
289 // Check if the lesson does not have pages.
290 if (!$lessonfirstpageid) {
291 $error = ["lessonnotready2" => null];
293 throw new moodle_exception(key($error), 'lesson');
295 $errors[key($error)] = current($error);
298 // Get the number of retries (also referenced as attempts), and the last page seen.
299 $attemptscount = $lesson->count_user_retries($USER->id
);
300 $lastpageseen = $lesson->get_last_page_seen($attemptscount);
302 // Check if the user left a timed session with no retakes.
303 if ($lastpageseen !== false && $lastpageseen != LESSON_EOL
) {
304 if ($lesson->left_during_timed_session($attemptscount) && $lesson->timelimit
&& !$lesson->retake
) {
305 $error = ["leftduringtimednoretake" => null];
307 throw new moodle_exception(key($error), 'lesson');
309 $errors[key($error)] = current($error);
311 } else if ($attemptscount > 0 && !$lesson->retake
) {
312 // The user finished the lesson and no retakes are allowed.
313 $error = ["noretake" => null];
315 throw new moodle_exception(key($error), 'lesson');
317 $errors[key($error)] = current($error);
320 if (!$timers = $lesson->get_user_timers($USER->id
, 'starttime DESC', '*', 0, 1)) {
321 $error = ["cannotfindtimer" => null];
323 throw new moodle_exception(key($error), 'lesson');
325 $errors[key($error)] = current($error);
327 $timer = current($timers);
328 if (!$lesson->check_time($timer)) {
329 $error = ["eolstudentoutoftime" => null];
331 throw new moodle_exception(key($error), 'lesson');
333 $errors[key($error)] = current($error);
336 // Check if the user want to review an attempt he just finished.
337 if (!empty($params['review'])) {
338 // Allow review only for completed attempts during active session time.
339 if ($timer->completed
and ($timer->lessontime +
$CFG->sessiontimeout
> time()) ) {
340 $ntries = $lesson->count_user_retries($USER->id
);
341 if ($attempts = $lesson->get_attempts($ntries)) {
342 $lastattempt = end($attempts);
343 $USER->modattempts
[$lesson->id
] = $lastattempt->pageid
;
347 if (!isset($USER->modattempts
[$lesson->id
])) {
348 $error = ["studentoutoftimeforreview" => null];
350 throw new moodle_exception(key($error), 'lesson');
352 $errors[key($error)] = current($error);
362 * Describes the parameters for get_lesson_access_information.
364 * @return external_external_function_parameters
367 public static function get_lesson_access_information_parameters() {
368 return new external_function_parameters (
370 'lessonid' => new external_value(PARAM_INT
, 'lesson instance id')
376 * Return access information for a given lesson.
378 * @param int $lessonid lesson instance id
379 * @return array of warnings and the access information
381 * @throws moodle_exception
383 public static function get_lesson_access_information($lessonid) {
389 'lessonid' => $lessonid
391 $params = self
::validate_parameters(self
::get_lesson_access_information_parameters(), $params);
393 list($lesson, $course, $cm, $context) = self
::validate_lesson($params['lessonid']);
396 // Capabilities first.
397 $result['canmanage'] = $lesson->can_manage();
398 $result['cangrade'] = has_capability('mod/lesson:grade', $context);
399 $result['canviewreports'] = has_capability('mod/lesson:viewreports', $context);
401 // Status information.
402 $result['reviewmode'] = $lesson->is_in_review_mode();
403 $result['attemptscount'] = $lesson->count_user_retries($USER->id
);
404 $lastpageseen = $lesson->get_last_page_seen($result['attemptscount']);
405 $result['lastpageseen'] = ($lastpageseen !== false) ?
$lastpageseen : 0;
406 $result['leftduringtimedsession'] = $lesson->left_during_timed_session($result['attemptscount']);
407 // To avoid multiple calls, store the magic property firstpage.
408 $lessonfirstpage = $lesson->firstpage
;
409 $result['firstpageid'] = $lessonfirstpage ?
$lessonfirstpage->id
: 0;
411 // Access restrictions now, we emulate a new attempt access to get the possible warnings.
412 $result['preventaccessreasons'] = [];
413 $validationerrors = self
::validate_attempt($lesson, ['password' => ''], true);
414 foreach ($validationerrors as $reason => $data) {
415 $result['preventaccessreasons'][] = [
418 'message' => get_string($reason, 'lesson', $data),
421 $result['warnings'] = $warnings;
426 * Describes the get_lesson_access_information return value.
428 * @return external_single_structure
431 public static function get_lesson_access_information_returns() {
432 return new external_single_structure(
434 'canmanage' => new external_value(PARAM_BOOL
, 'Whether the user can manage the lesson or not.'),
435 'cangrade' => new external_value(PARAM_BOOL
, 'Whether the user can grade the lesson or not.'),
436 'canviewreports' => new external_value(PARAM_BOOL
, 'Whether the user can view the lesson reports or not.'),
437 'reviewmode' => new external_value(PARAM_BOOL
, 'Whether the lesson is in review mode for the current user.'),
438 'attemptscount' => new external_value(PARAM_INT
, 'The number of attempts done by the user.'),
439 'lastpageseen' => new external_value(PARAM_INT
, 'The last page seen id.'),
440 'leftduringtimedsession' => new external_value(PARAM_BOOL
, 'Whether the user left during a timed session.'),
441 'firstpageid' => new external_value(PARAM_INT
, 'The lesson first page id.'),
442 'preventaccessreasons' => new external_multiple_structure(
443 new external_single_structure(
445 'reason' => new external_value(PARAM_ALPHANUMEXT
, 'Reason lang string code'),
446 'data' => new external_value(PARAM_RAW
, 'Additional data'),
447 'message' => new external_value(PARAM_RAW
, 'Complete html message'),
449 'The reasons why the user cannot attempt the lesson'
452 'warnings' => new external_warnings(),
458 * Describes the parameters for view_lesson.
460 * @return external_external_function_parameters
463 public static function view_lesson_parameters() {
464 return new external_function_parameters (
466 'lessonid' => new external_value(PARAM_INT
, 'lesson instance id'),
467 'password' => new external_value(PARAM_RAW
, 'lesson password', VALUE_DEFAULT
, ''),
473 * Trigger the course module viewed event and update the module completion status.
475 * @param int $lessonid lesson instance id
476 * @param str $password optional password (the lesson may be protected)
477 * @return array of warnings and status result
479 * @throws moodle_exception
481 public static function view_lesson($lessonid, $password = '') {
484 $params = array('lessonid' => $lessonid, 'password' => $password);
485 $params = self
::validate_parameters(self
::view_lesson_parameters(), $params);
488 list($lesson, $course, $cm, $context) = self
::validate_lesson($params['lessonid']);
489 self
::validate_attempt($lesson, $params);
491 $lesson->set_module_viewed();
494 $result['status'] = true;
495 $result['warnings'] = $warnings;
500 * Describes the view_lesson return value.
502 * @return external_single_structure
505 public static function view_lesson_returns() {
506 return new external_single_structure(
508 'status' => new external_value(PARAM_BOOL
, 'status: true if success'),
509 'warnings' => new external_warnings(),
515 * Check if the current user can retrieve lesson information (grades, attempts) about the given user.
517 * @param int $userid the user to check
518 * @param stdClass $course course object
519 * @param stdClass $cm cm object
520 * @param stdClass $context context object
521 * @throws moodle_exception
524 protected static function check_can_view_user_data($userid, $course, $cm, $context) {
525 $user = core_user
::get_user($userid, '*', MUST_EXIST
);
526 core_user
::require_active_user($user);
527 // Check permissions and that if users share group (if groups enabled).
528 require_capability('mod/lesson:viewreports', $context);
529 if (!groups_user_groups_visible($course, $user->id
, $cm)) {
530 throw new moodle_exception('notingroup');
535 * Describes the parameters for get_questions_attempts.
537 * @return external_external_function_parameters
540 public static function get_questions_attempts_parameters() {
541 return new external_function_parameters (
543 'lessonid' => new external_value(PARAM_INT
, 'lesson instance id'),
544 'attempt' => new external_value(PARAM_INT
, 'lesson attempt number'),
545 'correct' => new external_value(PARAM_BOOL
, 'only fetch correct attempts', VALUE_DEFAULT
, false),
546 'pageid' => new external_value(PARAM_INT
, 'only fetch attempts at the given page', VALUE_DEFAULT
, null),
547 'userid' => new external_value(PARAM_INT
, 'only fetch attempts of the given user', VALUE_DEFAULT
, null),
553 * Return the list of page question attempts in a given lesson.
555 * @param int $lessonid lesson instance id
556 * @param int $attempt the lesson attempt number
557 * @param bool $correct only fetch correct attempts
558 * @param int $pageid only fetch attempts at the given page
559 * @param int $userid only fetch attempts of the given user
560 * @return array of warnings and page attempts
562 * @throws moodle_exception
564 public static function get_questions_attempts($lessonid, $attempt, $correct = false, $pageid = null, $userid = null) {
568 'lessonid' => $lessonid,
569 'attempt' => $attempt,
570 'correct' => $correct,
574 $params = self
::validate_parameters(self
::get_questions_attempts_parameters(), $params);
577 list($lesson, $course, $cm, $context) = self
::validate_lesson($params['lessonid']);
579 // Default value for userid.
580 if (empty($params['userid'])) {
581 $params['userid'] = $USER->id
;
584 // Extra checks so only users with permissions can view other users attempts.
585 if ($USER->id
!= $params['userid']) {
586 self
::check_can_view_user_data($params['userid'], $course, $cm, $context);
590 $result['attempts'] = $lesson->get_attempts($params['attempt'], $params['correct'], $params['pageid'], $params['userid']);
591 $result['warnings'] = $warnings;
596 * Describes the get_questions_attempts return value.
598 * @return external_single_structure
601 public static function get_questions_attempts_returns() {
602 return new external_single_structure(
604 'attempts' => new external_multiple_structure(
605 new external_single_structure(
607 'id' => new external_value(PARAM_INT
, 'The attempt id'),
608 'lessonid' => new external_value(PARAM_INT
, 'The attempt lessonid'),
609 'pageid' => new external_value(PARAM_INT
, 'The attempt pageid'),
610 'userid' => new external_value(PARAM_INT
, 'The user who did the attempt'),
611 'answerid' => new external_value(PARAM_INT
, 'The attempt answerid'),
612 'retry' => new external_value(PARAM_INT
, 'The lesson attempt number'),
613 'correct' => new external_value(PARAM_INT
, 'If it was the correct answer'),
614 'useranswer' => new external_value(PARAM_RAW
, 'The complete user answer'),
615 'timeseen' => new external_value(PARAM_INT
, 'The time the question was seen'),
617 'The question page attempts'
620 'warnings' => new external_warnings(),
626 * Describes the parameters for get_user_grade.
628 * @return external_external_function_parameters
631 public static function get_user_grade_parameters() {
632 return new external_function_parameters (
634 'lessonid' => new external_value(PARAM_INT
, 'lesson instance id'),
635 'userid' => new external_value(PARAM_INT
, 'the user id (empty for current user)', VALUE_DEFAULT
, null),
641 * Return the final grade in the lesson for the given user.
643 * @param int $lessonid lesson instance id
644 * @param int $userid only fetch grades of this user
645 * @return array of warnings and page attempts
647 * @throws moodle_exception
649 public static function get_user_grade($lessonid, $userid = null) {
651 require_once($CFG->libdir
. '/gradelib.php');
654 'lessonid' => $lessonid,
657 $params = self
::validate_parameters(self
::get_user_grade_parameters(), $params);
660 list($lesson, $course, $cm, $context) = self
::validate_lesson($params['lessonid']);
662 // Default value for userid.
663 if (empty($params['userid'])) {
664 $params['userid'] = $USER->id
;
667 // Extra checks so only users with permissions can view other users attempts.
668 if ($USER->id
!= $params['userid']) {
669 self
::check_can_view_user_data($params['userid'], $course, $cm, $context);
673 $formattedgrade = null;
674 $grades = lesson_get_user_grades($lesson, $params['userid']);
675 if (!empty($grades)) {
676 $grade = $grades[$params['userid']]->rawgrade
;
679 'itemmodule' => 'lesson',
680 'iteminstance' => $lesson->id
,
681 'courseid' => $course->id
,
684 $gradeitem = grade_item
::fetch($params);
685 $formattedgrade = grade_format_gradevalue($grade, $gradeitem);
689 $result['grade'] = $grade;
690 $result['formattedgrade'] = $formattedgrade;
691 $result['warnings'] = $warnings;
696 * Describes the get_user_grade return value.
698 * @return external_single_structure
701 public static function get_user_grade_returns() {
702 return new external_single_structure(
704 'grade' => new external_value(PARAM_FLOAT
, 'The lesson final raw grade'),
705 'formattedgrade' => new external_value(PARAM_RAW
, 'The lesson final grade formatted'),
706 'warnings' => new external_warnings(),
712 * Describes the parameters for get_user_attempt_grade.
714 * @return external_external_function_parameters
717 public static function get_user_attempt_grade_parameters() {
718 return new external_function_parameters (
720 'lessonid' => new external_value(PARAM_INT
, 'lesson instance id'),
721 'lessonattempt' => new external_value(PARAM_INT
, 'lesson attempt number'),
722 'userid' => new external_value(PARAM_INT
, 'the user id (empty for current user)', VALUE_DEFAULT
, null),
728 * Return grade information in the attempt for a given user.
730 * @param int $lessonid lesson instance id
731 * @param int $lessonattempt lesson attempt number
732 * @param int $userid only fetch attempts of the given user
733 * @return array of warnings and page attempts
735 * @throws moodle_exception
737 public static function get_user_attempt_grade($lessonid, $lessonattempt, $userid = null) {
739 require_once($CFG->libdir
. '/gradelib.php');
742 'lessonid' => $lessonid,
743 'lessonattempt' => $lessonattempt,
746 $params = self
::validate_parameters(self
::get_user_attempt_grade_parameters(), $params);
749 list($lesson, $course, $cm, $context) = self
::validate_lesson($params['lessonid']);
751 // Default value for userid.
752 if (empty($params['userid'])) {
753 $params['userid'] = $USER->id
;
756 // Extra checks so only users with permissions can view other users attempts.
757 if ($USER->id
!= $params['userid']) {
758 self
::check_can_view_user_data($params['userid'], $course, $cm, $context);
761 $result = (array) lesson_grade($lesson, $params['lessonattempt'], $params['userid']);
762 $result['warnings'] = $warnings;
767 * Describes the get_user_attempt_grade return value.
769 * @return external_single_structure
772 public static function get_user_attempt_grade_returns() {
773 return new external_single_structure(
775 'nquestions' => new external_value(PARAM_INT
, 'Number of questions answered'),
776 'attempts' => new external_value(PARAM_INT
, 'Number of question attempts'),
777 'total' => new external_value(PARAM_FLOAT
, 'Max points possible'),
778 'earned' => new external_value(PARAM_FLOAT
, 'Points earned by student'),
779 'grade' => new external_value(PARAM_FLOAT
, 'Calculated percentage grade'),
780 'nmanual' => new external_value(PARAM_INT
, 'Number of manually graded questions'),
781 'manualpoints' => new external_value(PARAM_FLOAT
, 'Point value for manually graded questions'),
782 'warnings' => new external_warnings(),
788 * Describes the parameters for get_content_pages_viewed.
790 * @return external_external_function_parameters
793 public static function get_content_pages_viewed_parameters() {
794 return new external_function_parameters (
796 'lessonid' => new external_value(PARAM_INT
, 'lesson instance id'),
797 'lessonattempt' => new external_value(PARAM_INT
, 'lesson attempt number'),
798 'userid' => new external_value(PARAM_INT
, 'the user id (empty for current user)', VALUE_DEFAULT
, null),
804 * Return the list of content pages viewed by a user during a lesson attempt.
806 * @param int $lessonid lesson instance id
807 * @param int $lessonattempt lesson attempt number
808 * @param int $userid only fetch attempts of the given user
809 * @return array of warnings and page attempts
811 * @throws moodle_exception
813 public static function get_content_pages_viewed($lessonid, $lessonattempt, $userid = null) {
817 'lessonid' => $lessonid,
818 'lessonattempt' => $lessonattempt,
821 $params = self
::validate_parameters(self
::get_content_pages_viewed_parameters(), $params);
824 list($lesson, $course, $cm, $context) = self
::validate_lesson($params['lessonid']);
826 // Default value for userid.
827 if (empty($params['userid'])) {
828 $params['userid'] = $USER->id
;
831 // Extra checks so only users with permissions can view other users attempts.
832 if ($USER->id
!= $params['userid']) {
833 self
::check_can_view_user_data($params['userid'], $course, $cm, $context);
836 $pages = $lesson->get_content_pages_viewed($params['lessonattempt'], $params['userid']);
839 $result['pages'] = $pages;
840 $result['warnings'] = $warnings;
845 * Describes the get_content_pages_viewed return value.
847 * @return external_single_structure
850 public static function get_content_pages_viewed_returns() {
851 return new external_single_structure(
853 'pages' => new external_multiple_structure(
854 new external_single_structure(
856 'id' => new external_value(PARAM_INT
, 'The attempt id.'),
857 'lessonid' => new external_value(PARAM_INT
, 'The lesson id.'),
858 'pageid' => new external_value(PARAM_INT
, 'The page id.'),
859 'userid' => new external_value(PARAM_INT
, 'The user who viewed the page.'),
860 'retry' => new external_value(PARAM_INT
, 'The lesson attempt number.'),
861 'flag' => new external_value(PARAM_INT
, '1 if the next page was calculated randomly.'),
862 'timeseen' => new external_value(PARAM_INT
, 'The time the page was seen.'),
863 'nextpageid' => new external_value(PARAM_INT
, 'The next page chosen id.'),
865 'The content pages viewed.'
868 'warnings' => new external_warnings(),
874 * Describes the parameters for get_user_timers.
876 * @return external_external_function_parameters
879 public static function get_user_timers_parameters() {
880 return new external_function_parameters (
882 'lessonid' => new external_value(PARAM_INT
, 'lesson instance id'),
883 'userid' => new external_value(PARAM_INT
, 'the user id (empty for current user)', VALUE_DEFAULT
, null),
889 * Return the timers in the current lesson for the given user.
891 * @param int $lessonid lesson instance id
892 * @param int $userid only fetch timers of the given user
893 * @return array of warnings and timers
895 * @throws moodle_exception
897 public static function get_user_timers($lessonid, $userid = null) {
901 'lessonid' => $lessonid,
904 $params = self
::validate_parameters(self
::get_user_timers_parameters(), $params);
907 list($lesson, $course, $cm, $context) = self
::validate_lesson($params['lessonid']);
909 // Default value for userid.
910 if (empty($params['userid'])) {
911 $params['userid'] = $USER->id
;
914 // Extra checks so only users with permissions can view other users attempts.
915 if ($USER->id
!= $params['userid']) {
916 self
::check_can_view_user_data($params['userid'], $course, $cm, $context);
919 $timers = $lesson->get_user_timers($params['userid']);
922 $result['timers'] = $timers;
923 $result['warnings'] = $warnings;
928 * Describes the get_user_timers return value.
930 * @return external_single_structure
933 public static function get_user_timers_returns() {
934 return new external_single_structure(
936 'timers' => new external_multiple_structure(
937 new external_single_structure(
939 'id' => new external_value(PARAM_INT
, 'The attempt id'),
940 'lessonid' => new external_value(PARAM_INT
, 'The lesson id'),
941 'userid' => new external_value(PARAM_INT
, 'The user id'),
942 'starttime' => new external_value(PARAM_INT
, 'First access time for a new timer session'),
943 'lessontime' => new external_value(PARAM_INT
, 'Last access time to the lesson during the timer session'),
944 'completed' => new external_value(PARAM_INT
, 'If the lesson for this timer was completed'),
949 'warnings' => new external_warnings(),
955 * Describes the parameters for get_pages.
957 * @return external_external_function_parameters
960 public static function get_pages_parameters() {
961 return new external_function_parameters (
963 'lessonid' => new external_value(PARAM_INT
, 'lesson instance id'),
964 'password' => new external_value(PARAM_RAW
, 'optional password (the lesson may be protected)', VALUE_DEFAULT
, ''),
970 * Return the list of pages in a lesson (based on the user permissions).
972 * @param int $lessonid lesson instance id
973 * @param str $password optional password (the lesson may be protected)
974 * @return array of warnings and status result
976 * @throws moodle_exception
978 public static function get_pages($lessonid, $password = '') {
980 $params = array('lessonid' => $lessonid, 'password' => $password);
981 $params = self
::validate_parameters(self
::get_pages_parameters(), $params);
984 list($lesson, $course, $cm, $context) = self
::validate_lesson($params['lessonid']);
985 self
::validate_attempt($lesson, $params);
987 $lessonpages = $lesson->load_all_pages();
990 foreach ($lessonpages as $page) {
991 $pagedata = new stdClass
; // Contains the data that will be returned by the WS
993 // Return the visible data.
994 $visibleproperties = array('id', 'lessonid', 'prevpageid', 'nextpageid', 'qtype', 'qoption', 'layout', 'display',
995 'displayinmenublock', 'type', 'typeid', 'typestring', 'timecreated', 'timemodified');
996 foreach ($visibleproperties as $prop) {
997 $pagedata->{$prop} = $page->{$prop};
1000 // Check if we can see title (contents required custom rendering, we won't returning it here @see get_page_data).
1001 $canmanage = $lesson->can_manage();
1002 // If we are managers or the menu block is enabled and is a content page visible.
1003 if ($canmanage ||
(lesson_displayleftif($lesson) && $page->displayinmenublock
&& $page->display
)) {
1004 $pagedata->title
= external_format_string($page->title
, $context->id
);
1007 // Now, calculate the file area files (maybe we need to download a lesson for offline usage).
1008 $pagedata->filescount
= 0;
1009 $pagedata->filessizetotal
= 0;
1010 $files = $page->get_files(false); // Get files excluding directories.
1011 foreach ($files as $file) {
1012 $pagedata->filescount++
;
1013 $pagedata->filessizetotal +
= $file->get_filesize();
1016 // Now the possible answers and page jumps ids.
1017 $pagedata->answerids
= array();
1018 $pagedata->jumps
= array();
1019 $answers = $page->get_answers();
1020 foreach ($answers as $answer) {
1021 $pagedata->answerids
[] = $answer->id
;
1022 $pagedata->jumps
[] = $answer->jumpto
;
1023 $files = $answer->get_files(false); // Get files excluding directories.
1024 foreach ($files as $file) {
1025 $pagedata->filescount++
;
1026 $pagedata->filessizetotal +
= $file->get_filesize();
1029 $pages[] = $pagedata;
1033 $result['pages'] = $pages;
1034 $result['warnings'] = $warnings;
1039 * Describes the get_pages return value.
1041 * @return external_single_structure
1044 public static function get_pages_returns() {
1045 return new external_single_structure(
1047 'pages' => new external_multiple_structure(
1048 new external_single_structure(
1050 'id' => new external_value(PARAM_INT
, 'The id of this lesson page'),
1051 'lessonid' => new external_value(PARAM_INT
, 'The id of the lesson this page belongs to'),
1052 'prevpageid' => new external_value(PARAM_INT
, 'The id of the page before this one'),
1053 'nextpageid' => new external_value(PARAM_INT
, 'The id of the next page in the page sequence'),
1054 'qtype' => new external_value(PARAM_INT
, 'Identifies the page type of this page'),
1055 'qoption' => new external_value(PARAM_INT
, 'Used to record page type specific options'),
1056 'layout' => new external_value(PARAM_INT
, 'Used to record page specific layout selections'),
1057 'display' => new external_value(PARAM_INT
, 'Used to record page specific display selections'),
1058 'timecreated' => new external_value(PARAM_INT
, 'Timestamp for when the page was created'),
1059 'timemodified' => new external_value(PARAM_INT
, 'Timestamp for when the page was last modified'),
1060 'title' => new external_value(PARAM_RAW
, 'The title of this page', VALUE_OPTIONAL
),
1061 'displayinmenublock' => new external_value(PARAM_BOOL
, 'Toggles display in the left menu block'),
1062 'type' => new external_value(PARAM_INT
, 'The type of the page [question | structure]'),
1063 'typeid' => new external_value(PARAM_INT
, 'The unique identifier for the page type'),
1064 'typestring' => new external_value(PARAM_RAW
, 'The string that describes this page type'),
1065 'answerids' => new external_multiple_structure(
1066 new external_value(PARAM_INT
, 'Answer id'), 'List of answers ids (empty for content pages in Moodle 1.9)'
1068 'jumps' => new external_multiple_structure(
1069 new external_value(PARAM_INT
, 'Page to jump id'), 'List of possible page jumps'
1071 'filescount' => new external_value(PARAM_INT
, 'The total number of files attached to the page'),
1072 'filessizetotal' => new external_value(PARAM_INT
, 'The total size of the files'),
1077 'warnings' => new external_warnings(),
1083 * Describes the parameters for launch_attempt.
1085 * @return external_external_function_parameters
1088 public static function launch_attempt_parameters() {
1089 return new external_function_parameters (
1091 'lessonid' => new external_value(PARAM_INT
, 'lesson instance id'),
1092 'password' => new external_value(PARAM_RAW
, 'optional password (the lesson may be protected)', VALUE_DEFAULT
, ''),
1093 'pageid' => new external_value(PARAM_RAW
, 'page id to continue from (only when continuing an attempt)', VALUE_DEFAULT
, 0),
1094 'review' => new external_value(PARAM_RAW
, 'if we want to review just after finishing', VALUE_DEFAULT
, false),
1100 * Starts a new attempt or continues an existing one.
1102 * @param int $lessonid lesson instance id
1103 * @param str $password optional password (the lesson may be protected)
1104 * @param int $pageid page id to continue from (only when continuing an attempt)
1105 * @param bool $review if we want to review just after finishing
1106 * @return array of warnings and status result
1108 * @throws moodle_exception
1110 public static function launch_attempt($lessonid, $password = '', $pageid = 0, $review = false) {
1113 $params = array('lessonid' => $lessonid, 'password' => $password, 'pageid' => $pageid, 'review' => $review);
1114 $params = self
::validate_parameters(self
::launch_attempt_parameters(), $params);
1115 $warnings = $messages = array();
1117 list($lesson, $course, $cm, $context) = self
::validate_lesson($params['lessonid']);
1118 self
::validate_attempt($lesson, $params);
1121 // Starting a new lesson attempt.
1122 if (empty($params['pageid'])) {
1123 // Check if there is a recent timer created during the active session.
1124 $alreadystarted = false;
1125 if ($timers = $lesson->get_user_timers($USER->id
, 'starttime DESC', '*', 0, 1)) {
1126 $timer = array_shift($timers);
1127 $endtime = $lesson->timelimit
> 0 ?
min($CFG->sessiontimeout
, $lesson->timelimit
) : $CFG->sessiontimeout
;
1128 if (!$timer->completed
&& $timer->starttime
> time() - $endtime) {
1129 $alreadystarted = true;
1132 if (!$alreadystarted && !$lesson->can_manage()) {
1133 $lesson->start_timer();
1136 if ($params['pageid'] == LESSON_EOL
) {
1137 throw new moodle_exception('endoflesson', 'lesson');
1139 $timer = $lesson->update_timer(true, true);
1140 if (!$lesson->check_time($timer)) {
1141 throw new moodle_exception('eolstudentoutoftime', 'lesson');
1144 foreach ($lesson->messages
as $message) {
1145 $messages[] = array(
1146 'message' => $message[0],
1147 'type' => $message[1],
1153 'messages' => $messages,
1154 'warnings' => $warnings,
1160 * Describes the launch_attempt return value.
1162 * @return external_single_structure
1165 public static function launch_attempt_returns() {
1166 return new external_single_structure(
1168 'messages' => new external_multiple_structure(
1169 new external_single_structure(
1171 'message' => new external_value(PARAM_RAW
, 'Message'),
1172 'type' => new external_value(PARAM_ALPHANUMEXT
, 'Message type: usually a CSS identifier like:
1173 success, info, warning, error, notifyproblem, notifyerror, notifytiny, notifysuccess')
1174 ), 'The lesson generated messages'
1177 'warnings' => new external_warnings(),