Skip to content

Commit

Permalink
Merge branch 'MDL-57643-master' of git://github.com/jleyva/moodle
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Mar 15, 2017
2 parents eb6f047 + 630f0e3 commit 9c2ca40
Show file tree
Hide file tree
Showing 8 changed files with 482 additions and 66 deletions.
207 changes: 207 additions & 0 deletions mod/lesson/classes/external.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,211 @@ public static function get_lessons_by_courses_returns() {
)
);
}

/**
* Utility function for validating a lesson.
*
* @param int $lessonid lesson instance id
* @return array array containing the lesson, course, context and course module objects
* @since Moodle 3.3
*/
protected static function validate_lesson($lessonid) {
global $DB, $USER;

// Request and permission validation.
$lesson = $DB->get_record('lesson', array('id' => $lessonid), '*', MUST_EXIST);
list($course, $cm) = get_course_and_cm_from_instance($lesson, 'lesson');

$lesson = new lesson($lesson, $cm);
$lesson->update_effective_access($USER->id);

$context = $lesson->context;
self::validate_context($context);

return array($lesson, $course, $cm, $context);
}

/**
* Validates a new attempt.
*
* @param lesson $lesson lesson instance
* @param array $params request parameters
* @param boolean $return whether to return the errors or throw exceptions
* @return array the errors (if return set to true)
* @since Moodle 3.3
*/
protected static function validate_attempt(lesson $lesson, $params, $return = false) {
global $USER;

$errors = array();

// Avoid checkings for managers.
if ($lesson->can_manage()) {
return [];
}

// Dead line.
if ($timerestriction = $lesson->get_time_restriction_status()) {
$error = ["$timerestriction->reason" => userdate($timerestriction->time)];
if (!$return) {
throw new moodle_exception(key($error), 'lesson', '', current($error));
}
$errors[key($error)] = current($error);
}

// Password protected lesson code.
if ($passwordrestriction = $lesson->get_password_restriction_status($params['password'])) {
$error = ["passwordprotectedlesson" => external_format_string($lesson->name, $lesson->context->id)];
if (!$return) {
throw new moodle_exception(key($error), 'lesson', '', current($error));
}
$errors[key($error)] = current($error);
}

// Check for dependencies.
if ($dependenciesrestriction = $lesson->get_dependencies_restriction_status()) {
$errorhtmllist = implode(get_string('and', 'lesson') . ', ', $dependenciesrestriction->errors);
$error = ["completethefollowingconditions" => $dependenciesrestriction->dependentlesson->name . $errorhtmllist];
if (!$return) {
throw new moodle_exception(key($error), 'lesson', '', current($error));
}
$errors[key($error)] = current($error);
}

// To check only when no page is set (starting or continuing a lesson).
if (empty($params['pageid'])) {
// To avoid multiple calls, store the magic property firstpage.
$lessonfirstpage = $lesson->firstpage;
$lessonfirstpageid = $lessonfirstpage ? $lessonfirstpage->id : false;

// Check if the lesson does not have pages.
if (!$lessonfirstpageid) {
$error = ["lessonnotready2" => null];
if (!$return) {
throw new moodle_exception(key($error), 'lesson');
}
$errors[key($error)] = current($error);
}

// Get the number of retries (also referenced as attempts), and the last page seen.
$attemptscount = $lesson->count_user_retries($USER->id);
$lastpageseen = $lesson->get_last_page_seen($attemptscount);

// Check if the user left a timed session with no retakes.
if ($lastpageseen !== false && $lastpageseen != LESSON_EOL) {
if ($lesson->left_during_timed_session($attemptscount) && $lesson->timelimit && !$lesson->retake) {
$error = ["leftduringtimednoretake" => null];
if (!$return) {
throw new moodle_exception(key($error), 'lesson');
}
$errors[key($error)] = current($error);
}
} else if ($attemptscount > 0 && !$lesson->retake) {
// The user finished the lesson and no retakes are allowed.
$error = ["noretake" => null];
if (!$return) {
throw new moodle_exception(key($error), 'lesson');
}
$errors[key($error)] = current($error);
}
}

return $errors;
}

/**
* Describes the parameters for get_lesson_access_information.
*
* @return external_external_function_parameters
* @since Moodle 3.3
*/
public static function get_lesson_access_information_parameters() {
return new external_function_parameters (
array(
'lessonid' => new external_value(PARAM_INT, 'lesson instance id')
)
);
}

/**
* Return access information for a given lesson.
*
* @param int $lessonid lesson instance id
* @return array of warnings and the access information
* @since Moodle 3.3
* @throws moodle_exception
*/
public static function get_lesson_access_information($lessonid) {
global $DB, $USER;

$warnings = array();

$params = array(
'lessonid' => $lessonid
);
$params = self::validate_parameters(self::get_lesson_access_information_parameters(), $params);

list($lesson, $course, $cm, $context) = self::validate_lesson($params['lessonid']);

$result = array();
// Capabilities first.
$result['canmanage'] = $lesson->can_manage();
$result['cangrade'] = has_capability('mod/lesson:grade', $context);
$result['canviewreports'] = has_capability('mod/lesson:viewreports', $context);

// Status information.
$result['reviewmode'] = $lesson->is_in_review_mode();
$result['attemptscount'] = $lesson->count_user_retries($USER->id);
$lastpageseen = $lesson->get_last_page_seen($result['attemptscount']);
$result['lastpageseen'] = ($lastpageseen !== false) ? $lastpageseen : 0;
$result['leftduringtimedsession'] = $lesson->left_during_timed_session($result['attemptscount']);
// To avoid multiple calls, store the magic property firstpage.
$lessonfirstpage = $lesson->firstpage;
$result['firstpageid'] = $lessonfirstpage ? $lessonfirstpage->id : 0;

// Access restrictions now, we emulate a new attempt access to get the possible warnings.
$result['preventaccessreasons'] = [];
$validationerrors = self::validate_attempt($lesson, ['password' => ''], true);
foreach ($validationerrors as $reason => $data) {
$result['preventaccessreasons'][] = [
'reason' => $reason,
'data' => $data,
'message' => get_string($reason, 'lesson', $data),
];
}
$result['warnings'] = $warnings;
return $result;
}

/**
* Describes the get_lesson_access_information return value.
*
* @return external_single_structure
* @since Moodle 3.3
*/
public static function get_lesson_access_information_returns() {
return new external_single_structure(
array(
'canmanage' => new external_value(PARAM_BOOL, 'Whether the user can manage the lesson or not.'),
'cangrade' => new external_value(PARAM_BOOL, 'Whether the user can grade the lesson or not.'),
'canviewreports' => new external_value(PARAM_BOOL, 'Whether the user can view the lesson reports or not.'),
'reviewmode' => new external_value(PARAM_BOOL, 'Whether the lesson is in review mode for the current user.'),
'attemptscount' => new external_value(PARAM_INT, 'The number of attempts done by the user.'),
'lastpageseen' => new external_value(PARAM_INT, 'The last page seen id.'),
'leftduringtimedsession' => new external_value(PARAM_BOOL, 'Whether the user left during a timed session.'),
'firstpageid' => new external_value(PARAM_INT, 'The lesson first page id.'),
'preventaccessreasons' => new external_multiple_structure(
new external_single_structure(
array(
'reason' => new external_value(PARAM_ALPHANUMEXT, 'Reason lang string code'),
'data' => new external_value(PARAM_RAW, 'Additional data'),
'message' => new external_value(PARAM_RAW, 'Complete html message'),
),
'The reasons why the user cannot attempt the lesson'
)
),
'warnings' => new external_warnings(),
)
);
}
}
6 changes: 1 addition & 5 deletions mod/lesson/continue.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,7 @@
// record answer (if necessary) and show response (if none say if answer is correct or not)
$page = $lesson->load_page(required_param('pageid', PARAM_INT));

$userhasgrade = $DB->count_records("lesson_grades", array("lessonid"=>$lesson->id, "userid"=>$USER->id));
$reviewmode = false;
if ($userhasgrade && !$lesson->retake) {
$reviewmode = true;
}
$reviewmode = $lesson->is_in_review_mode();

// Check the page has answers [MDL-25632]
if (count($page->answers) > 0) {
Expand Down
11 changes: 10 additions & 1 deletion mod/lesson/db/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,18 @@
'mod_lesson_get_lessons_by_courses' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_lessons_by_courses',
'description' => 'Returns a list of lessons in a provided list of courses, if no list is provided all lessons that the user can view will be returned.',
'description' => 'Returns a list of lessons in a provided list of courses,
if no list is provided all lessons that the user can view will be returned.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'mod_lesson_get_lesson_access_information' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_lesson_access_information',
'description' => 'Return access information for a given lesson.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
);
2 changes: 1 addition & 1 deletion mod/lesson/essay.php
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@
$essaylinks = array();

// Number of attempts on the lesson
$attempts = $DB->count_records('lesson_grades', array('userid'=>$userid, 'lessonid'=>$lesson->id));
$attempts = $lesson->count_user_retries($userid);

// Go through each essay page
foreach ($studentessays[$userid] as $page => $tries) {
Expand Down
102 changes: 102 additions & 0 deletions mod/lesson/locallib.php
Original file line number Diff line number Diff line change
Expand Up @@ -2255,6 +2255,108 @@ public function get_dependencies_restriction_status() {
}
return false;
}

/**
* Check if the lesson is in review mode. (The user already finished it and retakes are not allowed).
*
* @return bool true if is in review mode
* @since Moodle 3.3
*/
public function is_in_review_mode() {
global $DB, $USER;

$userhasgrade = $DB->count_records("lesson_grades", array("lessonid" => $this->properties->id, "userid" => $USER->id));
if ($userhasgrade && !$this->properties->retake) {
return true;
}
return false;
}

/**
* Return the last page the current user saw.
*
* @param int $retriescount the number of retries for the lesson (the last retry number).
* @return mixed false if the user didn't see the lesson or the last page id
*/
public function get_last_page_seen($retriescount) {
global $DB, $USER;

$lastpageseen = false;
$allattempts = $this->get_attempts($retriescount);
if (!empty($allattempts)) {
$attempt = end($allattempts);
$attemptpage = $this->load_page($attempt->pageid);
$jumpto = $DB->get_field('lesson_answers', 'jumpto', array('id' => $attempt->answerid));
// Convert the jumpto to a proper page id.
if ($jumpto == 0) {
// Check if a question has been incorrectly answered AND no more attempts at it are left.
$nattempts = $this->get_attempts($attempt->retry, false, $attempt->pageid, $USER->id);
if (count($nattempts) >= $this->properties->maxattempts) {
$lastpageseen = $this->get_next_page($attemptpage->nextpageid);
} else {
$lastpageseen = $attempt->pageid;
}
} else if ($jumpto == LESSON_NEXTPAGE) {
$lastpageseen = $this->get_next_page($attemptpage->nextpageid);
} else if ($jumpto == LESSON_CLUSTERJUMP) {
$lastpageseen = $this->cluster_jump($attempt->pageid);
} else {
$lastpageseen = $jumpto;
}
}

if ($branchtables = $DB->get_records('lesson_branch', array("lessonid" => $this->properties->id, "userid" => $USER->id,
"retry" => $retriescount), 'timeseen DESC')) {
// In here, user has viewed a branch table.
$lastbranchtable = current($branchtables);
if (count($allattempts) > 0) {
if ($lastbranchtable->timeseen > $attempt->timeseen) {
// This branch table was viewed more recently than the question page.
if (!empty($lastbranchtable->nextpageid)) {
$lastpageseen = $lastbranchtable->nextpageid;
} else {
// Next page ID did not exist prior to MDL-34006.
$lastpageseen = $lastbranchtable->pageid;
}
}
} else {
// Has not answered any questions but has viewed a branch table.
if (!empty($lastbranchtable->nextpageid)) {
$lastpageseen = $lastbranchtable->nextpageid;
} else {
// Next page ID did not exist prior to MDL-34006.
$lastpageseen = $lastbranchtable->pageid;
}
}
}
return $lastpageseen;
}

/**
* Return the number of retries in a lesson for a given user.
*
* @param int $userid the user id
* @return int the retries count
* @since Moodle 3.3
*/
public function count_user_retries($userid) {
global $DB;

return $DB->count_records('lesson_grades', array("lessonid" => $this->properties->id, "userid" => $userid));
}

/**
* Check if a user left a timed session.
*
* @param int $retriescount the number of retries for the lesson (the last retry number).
* @return true if the user left the timed session
*/
public function left_during_timed_session($retriescount) {
global $DB, $USER;

$conditions = array('lessonid' => $this->properties->id, 'userid' => $USER->id, 'retry' => $retriescount);
return $DB->count_records('lesson_attempts', $conditions) > 0 || $DB->count_records('lesson_branch', $conditions) > 0;
}
}


Expand Down
Loading

0 comments on commit 9c2ca40

Please sign in to comment.