mirror of
https://github.com/moodle/moodle.git
synced 2025-03-17 22:20:00 +01:00
MDL-57643 mod_lesson: New WS mod_lesson_get_access_information
This commit is contained in:
parent
37029e468f
commit
630f0e3bce
@ -209,4 +209,211 @@ class mod_lesson_external extends external_api {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,18 @@ $functions = array(
|
||||
'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),
|
||||
),
|
||||
);
|
||||
|
@ -31,6 +31,30 @@ global $CFG;
|
||||
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
require_once($CFG->dirroot . '/mod/lesson/locallib.php');
|
||||
|
||||
/**
|
||||
* Silly class to access mod_lesson_external internal methods.
|
||||
*
|
||||
* @package mod_lesson
|
||||
* @copyright 2017 Juan Leyva <juan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 3.3
|
||||
*/
|
||||
class testable_mod_lesson_external extends mod_lesson_external {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public static function validate_attempt(lesson $lesson, $params, $return = false) {
|
||||
return parent::validate_attempt($lesson, $params, $return);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lesson module external functions tests
|
||||
*
|
||||
@ -53,6 +77,9 @@ class mod_lesson_external_testcase extends externallib_advanced_testcase {
|
||||
// Setup test data.
|
||||
$this->course = $this->getDataGenerator()->create_course();
|
||||
$this->lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $this->course->id));
|
||||
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
|
||||
$this->page1 = $lessongenerator->create_content($this->lesson);
|
||||
$this->page2 = $lessongenerator->create_question_truefalse($this->lesson);
|
||||
$this->context = context_module::instance($this->lesson->cmid);
|
||||
$this->cm = get_coursemodule_from_instance('lesson', $this->lesson->id);
|
||||
|
||||
@ -192,4 +219,124 @@ class mod_lesson_external_testcase extends externallib_advanced_testcase {
|
||||
$this->assertFalse(isset($lessons['lessons'][0]['intro']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the validate_attempt function.
|
||||
*/
|
||||
public function test_validate_attempt() {
|
||||
global $DB;
|
||||
|
||||
$this->setUser($this->student);
|
||||
// Test deadline.
|
||||
$oldtime = time() - DAYSECS;
|
||||
$DB->set_field('lesson', 'deadline', $oldtime, array('id' => $this->lesson->id));
|
||||
|
||||
$lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
|
||||
$validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
|
||||
$this->assertEquals('lessonclosed', key($validation));
|
||||
$this->assertCount(1, $validation);
|
||||
|
||||
// Test not available yet.
|
||||
$futuretime = time() + DAYSECS;
|
||||
$DB->set_field('lesson', 'deadline', 0, array('id' => $this->lesson->id));
|
||||
$DB->set_field('lesson', 'available', $futuretime, array('id' => $this->lesson->id));
|
||||
|
||||
$lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
|
||||
$validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
|
||||
$this->assertEquals('lessonopen', key($validation));
|
||||
$this->assertCount(1, $validation);
|
||||
|
||||
// Test password.
|
||||
$DB->set_field('lesson', 'deadline', 0, array('id' => $this->lesson->id));
|
||||
$DB->set_field('lesson', 'available', 0, array('id' => $this->lesson->id));
|
||||
$DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id));
|
||||
$DB->set_field('lesson', 'password', 'abc', array('id' => $this->lesson->id));
|
||||
|
||||
$lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
|
||||
$validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
|
||||
$this->assertEquals('passwordprotectedlesson', key($validation));
|
||||
$this->assertCount(1, $validation);
|
||||
|
||||
$lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
|
||||
$validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => 'abc'], true);
|
||||
$this->assertCount(0, $validation);
|
||||
|
||||
// Dependencies.
|
||||
$record = new stdClass();
|
||||
$record->course = $this->course->id;
|
||||
$lesson2 = self::getDataGenerator()->create_module('lesson', $record);
|
||||
$DB->set_field('lesson', 'usepassword', 0, array('id' => $this->lesson->id));
|
||||
$DB->set_field('lesson', 'password', '', array('id' => $this->lesson->id));
|
||||
$DB->set_field('lesson', 'dependency', $lesson->id, array('id' => $this->lesson->id));
|
||||
|
||||
$lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
|
||||
$lesson->conditions = serialize((object) ['completed' => true, 'timespent' => 0, 'gradebetterthan' => 0]);
|
||||
$validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
|
||||
$this->assertEquals('completethefollowingconditions', key($validation));
|
||||
$this->assertCount(1, $validation);
|
||||
|
||||
// Lesson withou pages.
|
||||
$lesson = new lesson($lesson2);
|
||||
$validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
|
||||
$this->assertEquals('lessonnotready2', key($validation));
|
||||
$this->assertCount(1, $validation);
|
||||
|
||||
// Test retakes.
|
||||
$DB->set_field('lesson', 'dependency', 0, array('id' => $this->lesson->id));
|
||||
$DB->set_field('lesson', 'retake', 0, array('id' => $this->lesson->id));
|
||||
$record = [
|
||||
'lessonid' => $this->lesson->id,
|
||||
'userid' => $this->student->id,
|
||||
'grade' => 100,
|
||||
'late' => 0,
|
||||
'completed' => 1,
|
||||
];
|
||||
$DB->insert_record('lesson_grades', (object) $record);
|
||||
$lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
|
||||
$validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
|
||||
$this->assertEquals('noretake', key($validation));
|
||||
$this->assertCount(1, $validation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the get_lesson_access_information function.
|
||||
*/
|
||||
public function test_get_lesson_access_information() {
|
||||
global $DB;
|
||||
|
||||
$this->setUser($this->student);
|
||||
// Add previous attempt.
|
||||
$record = [
|
||||
'lessonid' => $this->lesson->id,
|
||||
'userid' => $this->student->id,
|
||||
'grade' => 100,
|
||||
'late' => 0,
|
||||
'completed' => 1,
|
||||
];
|
||||
$DB->insert_record('lesson_grades', (object) $record);
|
||||
|
||||
$result = mod_lesson_external::get_lesson_access_information($this->lesson->id);
|
||||
$result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result);
|
||||
$this->assertFalse($result['canmanage']);
|
||||
$this->assertFalse($result['cangrade']);
|
||||
$this->assertFalse($result['canviewreports']);
|
||||
|
||||
$this->assertFalse($result['leftduringtimedsession']);
|
||||
$this->assertEquals(1, $result['reviewmode']);
|
||||
$this->assertEquals(1, $result['attemptscount']);
|
||||
$this->assertEquals(0, $result['lastpageseen']);
|
||||
$this->assertEquals($this->page2->id, $result['firstpageid']);
|
||||
$this->assertCount(1, $result['preventaccessreasons']);
|
||||
$this->assertEquals('noretake', $result['preventaccessreasons'][0]['reason']);
|
||||
$this->assertEquals(null, $result['preventaccessreasons'][0]['data']);
|
||||
$this->assertEquals(get_string('noretake', 'lesson'), $result['preventaccessreasons'][0]['message']);
|
||||
|
||||
// Now check permissions as admin.
|
||||
$this->setAdminUser();
|
||||
$result = mod_lesson_external::get_lesson_access_information($this->lesson->id);
|
||||
$result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result);
|
||||
$this->assertTrue($result['canmanage']);
|
||||
$this->assertTrue($result['cangrade']);
|
||||
$this->assertTrue($result['canviewreports']);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2016120501; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2016120502; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2016112900; // Requires this Moodle version
|
||||
$plugin->component = 'mod_lesson'; // Full name of the plugin (used for diagnostics)
|
||||
$plugin->cron = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user