diff --git a/mod/lesson/classes/external.php b/mod/lesson/classes/external.php new file mode 100644 index 00000000000..f3d10518efa --- /dev/null +++ b/mod/lesson/classes/external.php @@ -0,0 +1,212 @@ +. + +/** + * Lesson external API + * + * @package mod_lesson + * @category external + * @copyright 2017 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 3.3 + */ + +defined('MOODLE_INTERNAL') || die; + +require_once($CFG->libdir . '/externallib.php'); +require_once($CFG->dirroot . '/mod/lesson/locallib.php'); + +/** + * Lesson external functions + * + * @package mod_lesson + * @category external + * @copyright 2017 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 3.3 + */ +class mod_lesson_external extends external_api { + + /** + * Describes the parameters for get_lessons_by_courses. + * + * @return external_function_parameters + * @since Moodle 3.3 + */ + public static function get_lessons_by_courses_parameters() { + return new external_function_parameters ( + array( + 'courseids' => new external_multiple_structure( + new external_value(PARAM_INT, 'course id'), 'Array of course ids', VALUE_DEFAULT, array() + ), + ) + ); + } + + /** + * 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. + * + * @param array $courseids Array of course ids + * @return array of lessons details + * @since Moodle 3.3 + */ + public static function get_lessons_by_courses($courseids = array()) { + global $USER; + + $warnings = array(); + $returnedlessons = array(); + + $params = array( + 'courseids' => $courseids, + ); + $params = self::validate_parameters(self::get_lessons_by_courses_parameters(), $params); + + $mycourses = array(); + if (empty($params['courseids'])) { + $mycourses = enrol_get_my_courses(); + $params['courseids'] = array_keys($mycourses); + } + + // Ensure there are courseids to loop through. + if (!empty($params['courseids'])) { + + list($courses, $warnings) = external_util::validate_courses($params['courseids'], $mycourses); + + // Get the lessons in this course, this function checks users visibility permissions. + // We can avoid then additional validate_context calls. + $lessons = get_all_instances_in_courses("lesson", $courses); + foreach ($lessons as $lesson) { + $context = context_module::instance($lesson->coursemodule); + + $lesson = new lesson($lesson); + $lesson->update_effective_access($USER->id); + + // Entry to return. + $lessondetails = array(); + // First, we return information that any user can see in the web interface. + $lessondetails['id'] = $lesson->id; + $lessondetails['coursemodule'] = $lesson->coursemodule; + $lessondetails['course'] = $lesson->course; + $lessondetails['name'] = external_format_string($lesson->name, $context->id); + + $lessonavailable = $lesson->get_time_restriction_status() === false; + $lessonavailable = $lessonavailable && $lesson->get_password_restriction_status('') === false; + $lessonavailable = $lessonavailable && $lesson->get_dependencies_restriction_status() === false; + + if ($lessonavailable) { + // Format intro. + list($lessondetails['intro'], $lessondetails['introformat']) = external_format_text($lesson->intro, + $lesson->introformat, $context->id, 'mod_lesson', 'intro', null); + + $lessondetails['introfiles'] = external_util::get_area_files($context->id, 'mod_lesson', 'intro', false, false); + $lessondetails['mediafiles'] = external_util::get_area_files($context->id, 'mod_lesson', 'mediafile', 0); + $viewablefields = array('practice', 'modattempts', 'usepassword', 'grade', 'custom', 'ongoing', 'usemaxgrade', + 'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback', 'minquestions', + 'maxpages', 'timelimit', 'retake', 'mediafile', 'mediaheight', 'mediawidth', + 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif', + 'progressbar'); + + // Fields only for managers. + if ($lesson->can_manage()) { + $additionalfields = array('password', 'dependency', 'conditions', 'activitylink', 'available', 'deadline', + 'timemodified', 'completionendreached', 'completiontimespent'); + $viewablefields = array_merge($viewablefields, $additionalfields); + } + + foreach ($viewablefields as $field) { + $lessondetails[$field] = $lesson->{$field}; + } + } + $returnedlessons[] = $lessondetails; + } + } + $result = array(); + $result['lessons'] = $returnedlessons; + $result['warnings'] = $warnings; + return $result; + } + + /** + * Describes the get_lessons_by_courses return value. + * + * @return external_single_structure + * @since Moodle 3.3 + */ + public static function get_lessons_by_courses_returns() { + return new external_single_structure( + array( + 'lessons' => new external_multiple_structure( + new external_single_structure( + array( + 'id' => new external_value(PARAM_INT, 'Standard Moodle primary key.'), + 'course' => new external_value(PARAM_INT, 'Foreign key reference to the course this lesson is part of.'), + 'coursemodule' => new external_value(PARAM_INT, 'Course module id.'), + 'name' => new external_value(PARAM_RAW, 'Lesson name.'), + 'intro' => new external_value(PARAM_RAW, 'Lesson introduction text.', VALUE_OPTIONAL), + 'introformat' => new external_format_value('intro', VALUE_OPTIONAL), + 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL), + 'practice' => new external_value(PARAM_INT, 'Practice lesson?', VALUE_OPTIONAL), + 'modattempts' => new external_value(PARAM_INT, 'Allow student review?', VALUE_OPTIONAL), + 'usepassword' => new external_value(PARAM_INT, 'Password protected lesson?', VALUE_OPTIONAL), + 'password' => new external_value(PARAM_RAW, 'Password', VALUE_OPTIONAL), + 'dependency' => new external_value(PARAM_INT, 'Dependent on (another lesson id)', VALUE_OPTIONAL), + 'conditions' => new external_value(PARAM_RAW, 'Conditions to enable the lesson', VALUE_OPTIONAL), + 'grade' => new external_value(PARAM_INT, 'The total that the grade is scaled to be out of', + VALUE_OPTIONAL), + 'custom' => new external_value(PARAM_INT, 'Custom scoring?', VALUE_OPTIONAL), + 'ongoing' => new external_value(PARAM_INT, 'Display ongoing score?', VALUE_OPTIONAL), + 'usemaxgrade' => new external_value(PARAM_INT, 'How to calculate the final grade', VALUE_OPTIONAL), + 'maxanswers' => new external_value(PARAM_INT, 'Maximum answers per page', VALUE_OPTIONAL), + 'maxattempts' => new external_value(PARAM_INT, 'Maximum attempts', VALUE_OPTIONAL), + 'review' => new external_value(PARAM_INT, 'Provide option to try a question again', VALUE_OPTIONAL), + 'nextpagedefault' => new external_value(PARAM_INT, 'Action for a correct answer', VALUE_OPTIONAL), + 'feedback' => new external_value(PARAM_INT, 'Display default feedback', VALUE_OPTIONAL), + 'minquestions' => new external_value(PARAM_INT, 'Minimum number of questions', VALUE_OPTIONAL), + 'maxpages' => new external_value(PARAM_INT, 'Number of pages to show', VALUE_OPTIONAL), + 'timelimit' => new external_value(PARAM_INT, 'Time limit', VALUE_OPTIONAL), + 'retake' => new external_value(PARAM_INT, 'Re-takes allowed', VALUE_OPTIONAL), + 'activitylink' => new external_value(PARAM_INT, 'Link to next activity', VALUE_OPTIONAL), + 'mediafile' => new external_value(PARAM_RAW, 'Local file path or full external URL', VALUE_OPTIONAL), + 'mediafiles' => new external_files('Media files', VALUE_OPTIONAL), + 'mediaheight' => new external_value(PARAM_INT, 'Popup for media file height', VALUE_OPTIONAL), + 'mediawidth' => new external_value(PARAM_INT, 'Popup for media with', VALUE_OPTIONAL), + 'mediaclose' => new external_value(PARAM_INT, 'Display a close button in the popup?', VALUE_OPTIONAL), + 'slideshow' => new external_value(PARAM_INT, 'Display lesson as slideshow', VALUE_OPTIONAL), + 'width' => new external_value(PARAM_INT, 'Slideshow width', VALUE_OPTIONAL), + 'height' => new external_value(PARAM_INT, 'Slideshow height', VALUE_OPTIONAL), + 'bgcolor' => new external_value(PARAM_TEXT, 'Slideshow bgcolor', VALUE_OPTIONAL), + 'displayleft' => new external_value(PARAM_INT, 'Display left pages menu?', VALUE_OPTIONAL), + 'displayleftif' => new external_value(PARAM_INT, 'Minimum grade to display menu', VALUE_OPTIONAL), + 'progressbar' => new external_value(PARAM_INT, 'Display progress bar?', VALUE_OPTIONAL), + 'available' => new external_value(PARAM_INT, 'Available from', VALUE_OPTIONAL), + 'deadline' => new external_value(PARAM_INT, 'Available until', VALUE_OPTIONAL), + 'timemodified' => new external_value(PARAM_INT, 'Last time settings were updated', VALUE_OPTIONAL), + 'completionendreached' => new external_value(PARAM_INT, 'Require end reached for completion?', + VALUE_OPTIONAL), + 'completiontimespent' => new external_value(PARAM_INT, 'Student must do this activity at least for', + VALUE_OPTIONAL), + 'visible' => new external_value(PARAM_INT, 'Visible?', VALUE_OPTIONAL), + 'groupmode' => new external_value(PARAM_INT, 'Group mode', VALUE_OPTIONAL), + 'groupingid' => new external_value(PARAM_INT, 'Grouping id', VALUE_OPTIONAL), + ) + ) + ), + 'warnings' => new external_warnings(), + ) + ); + } +} diff --git a/mod/lesson/db/services.php b/mod/lesson/db/services.php new file mode 100644 index 00000000000..06284be2842 --- /dev/null +++ b/mod/lesson/db/services.php @@ -0,0 +1,38 @@ +. + +/** + * Lesson external functions and service definitions. + * + * @package mod_lesson + * @category external + * @copyright 2017 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 3.3 + */ + +defined('MOODLE_INTERNAL') || die; + +$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.', + 'type' => 'read', + 'capabilities' => 'mod/lesson:view', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ), +); diff --git a/mod/lesson/locallib.php b/mod/lesson/locallib.php index 9a973c7f102..e5e89628ec0 100644 --- a/mod/lesson/locallib.php +++ b/mod/lesson/locallib.php @@ -1010,6 +1010,32 @@ class lesson extends lesson_base { */ protected $loadedallpages = false; + /** + * Course module object gets set and retrieved by directly calling $lesson->cm; + * @see get_cm() + * @var stdClass + */ + protected $cm = null; + + /** + * Context object gets set and retrieved by directly calling $lesson->context; + * @see get_context() + * @var stdClass + */ + protected $context = null; + + /** + * Constructor method + * + * @param object $properties + * @param stdClass $cm course module object + * @since Moodle 3.3 + */ + public function __construct($properties, $cm = null) { + parent::__construct($properties); + $this->cm = $cm; + } + /** * Simply generates a lesson object given an array/object of properties * Overrides {@see lesson_base->create()} @@ -2066,6 +2092,169 @@ class lesson extends lesson_base { } } + + /** + * Return the lesson context object. + * + * @return stdClass context + * @since Moodle 3.3 + */ + public function get_context() { + if ($this->context == null) { + $this->context = context_module::instance($this->get_cm()->id); + } + return $this->context; + } + + /** + * Set the lesson course module object. + * + * @param stdClass $cm course module objct + * @since Moodle 3.3 + */ + private function set_cm($cm) { + $this->cm = $cm; + } + + /** + * Return the lesson course module object. + * + * @return stdClass course module + * @since Moodle 3.3 + */ + public function get_cm() { + if ($this->cm == null) { + $this->cm = get_coursemodule_from_instance('lesson', $this->properties->id); + } + return $this->cm; + } + + /** + * Check if the user can manage the lesson activity. + * + * @return bool true if the user can manage the lesson + * @since Moodle 3.3 + */ + public function can_manage() { + return has_capability('mod/lesson:manage', $this->get_context()); + } + + /** + * Check if time restriction is applied. + * + * @return mixed false if there aren't restrictions or an object with the restriction information + * @since Moodle 3.3 + */ + public function get_time_restriction_status() { + if ($this->can_manage()) { + return false; + } + + if (!$this->is_accessible()) { + if ($this->properties->deadline != 0 && time() > $this->properties->deadline) { + $status = ['reason' => 'lessonclosed', 'time' => $this->properties->deadline]; + } else { + $status = ['reason' => 'lessonopen', 'time' => $this->properties->available]; + } + return (object) $status; + } + return false; + } + + /** + * Check if password restriction is applied. + * + * @param string $userpassword the user password to check (if the restriction is set) + * @return mixed false if there aren't restrictions or an object with the restriction information + * @since Moodle 3.3 + */ + public function get_password_restriction_status($userpassword) { + global $USER; + if ($this->can_manage()) { + return false; + } + + if ($this->properties->usepassword && empty($USER->lessonloggedin[$this->id])) { + $correctpass = false; + if (!empty($userpassword) && + (($this->properties->password == md5(trim($userpassword))) || ($this->properties->password == trim($userpassword)))) { + // With or without md5 for backward compatibility (MDL-11090). + $correctpass = true; + $USER->lessonloggedin[$this->id] = true; + } else if (isset($this->properties->extrapasswords)) { + // Group overrides may have additional passwords. + foreach ($this->properties->extrapasswords as $password) { + if (strcmp($password, md5(trim($userpassword))) === 0 || strcmp($password, trim($userpassword)) === 0) { + $correctpass = true; + $USER->lessonloggedin[$this->id] = true; + } + } + } + return !$correctpass; + } + return false; + } + + /** + * Check if dependencies restrictions are applied. + * + * @return mixed false if there aren't restrictions or an object with the restriction information + * @since Moodle 3.3 + */ + public function get_dependencies_restriction_status() { + global $DB, $USER; + if ($this->can_manage()) { + return false; + } + + if ($dependentlesson = $DB->get_record('lesson', array('id' => $this->properties->dependency))) { + // Lesson exists, so we can proceed. + $conditions = unserialize($this->properties->conditions); + // Assume false for all. + $errors = array(); + // Check for the timespent condition. + if ($conditions->timespent) { + $timespent = false; + if ($attempttimes = $DB->get_records('lesson_timer', array("userid" => $USER->id, "lessonid" => $dependentlesson->id))) { + // Go through all the times and test to see if any of them satisfy the condition. + foreach ($attempttimes as $attempttime) { + $duration = $attempttime->lessontime - $attempttime->starttime; + if ($conditions->timespent < $duration / 60) { + $timespent = true; + } + } + } + if (!$timespent) { + $errors[] = get_string('timespenterror', 'lesson', $conditions->timespent); + } + } + // Check for the gradebetterthan condition. + if ($conditions->gradebetterthan) { + $gradebetterthan = false; + if ($studentgrades = $DB->get_records('lesson_grades', array("userid" => $USER->id, "lessonid" => $dependentlesson->id))) { + // Go through all the grades and test to see if any of them satisfy the condition. + foreach ($studentgrades as $studentgrade) { + if ($studentgrade->grade >= $conditions->gradebetterthan) { + $gradebetterthan = true; + } + } + } + if (!$gradebetterthan) { + $errors[] = get_string('gradebetterthanerror', 'lesson', $conditions->gradebetterthan); + } + } + // Check for the completed condition. + if ($conditions->completed) { + if (!$DB->count_records('lesson_grades', array('userid' => $USER->id, 'lessonid' => $dependentlesson->id))) { + $errors[] = get_string('completederror', 'lesson'); + } + } + if (!empty($errors)) { + return (object) ['errors' => $errors, 'dependentlesson' => $dependentlesson]; + } + } + return false; + } } diff --git a/mod/lesson/mediafile.php b/mod/lesson/mediafile.php index 65755ba8682..a6ac44f4954 100644 --- a/mod/lesson/mediafile.php +++ b/mod/lesson/mediafile.php @@ -34,16 +34,15 @@ $printclose = optional_param('printclose', 0, PARAM_INT); $cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST); $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); -$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST)); +$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST), $cm); require_login($course, false, $cm); - // Apply overrides. $lesson->update_effective_access($USER->id); -$context = context_module::instance($cm->id); -$canmanage = has_capability('mod/lesson:manage', $context); +$context = $lesson->context; +$canmanage = $lesson->can_manage(); $url = new moodle_url('/mod/lesson/mediafile.php', array('id'=>$id)); if ($printclose !== '') { @@ -67,49 +66,26 @@ if ($printclose) { // this is for framesets exit(); } -echo $lessonoutput->header($lesson, $cm); - -//TODO: this is copied from view.php - the access should be the same! -/// Check these for students only TODO: Find a better method for doing this! -/// Check lesson availability -/// Check for password -/// Check dependencies -if (!$canmanage) { - if (!$lesson->is_accessible()) { // Deadline restrictions - echo $lessonoutput->header($lesson, $cm); - if ($lesson->deadline != 0 && time() > $lesson->deadline) { - echo $lessonoutput->lesson_inaccessible(get_string('lessonclosed', 'lesson', userdate($lesson->deadline))); - } else { - echo $lessonoutput->lesson_inaccessible(get_string('lessonopen', 'lesson', userdate($lesson->available))); - } - echo $lessonoutput->footer(); - exit(); - } else if ($lesson->usepassword && empty($USER->lessonloggedin[$lesson->id])) { // Password protected lesson code - $correctpass = false; - if (!empty($userpassword) && (($lesson->password == md5(trim($userpassword))) || ($lesson->password == trim($userpassword)))) { - require_sesskey(); - // with or without md5 for backward compatibility (MDL-11090) - $USER->lessonloggedin[$lesson->id] = true; - $correctpass = true; - } else if (isset($lesson->extrapasswords)) { - // Group overrides may have additional passwords. - foreach ($lesson->extrapasswords as $password) { - if (strcmp($password, md5(trim($userpassword))) === 0 || strcmp($password, trim($userpassword)) === 0) { - require_sesskey(); - $correctpass = true; - $USER->lessonloggedin[$lesson->id] = true; - } - } - } - if (!$correctpass) { - echo $lessonoutput->header($lesson, $cm); - echo $lessonoutput->login_prompt($lesson, $userpassword !== ''); - echo $lessonoutput->footer(); - exit(); - } - } +// Check access restrictions. +if ($timerestriction = $lesson->get_time_restriction_status()) { // Deadline restrictions. + echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('notavailable')); + echo $lessonoutput->lesson_inaccessible(get_string($timerestriction->reason, 'lesson', userdate($timerestriction->time))); + echo $lessonoutput->footer(); + exit(); +} else if ($passwordrestriction = $lesson->get_password_restriction_status(null)) { // Password protected lesson code. + echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('passwordprotectedlesson', 'lesson', format_string($lesson->name))); + echo $lessonoutput->login_prompt($lesson, $userpassword !== ''); + echo $lessonoutput->footer(); + exit(); +} else if ($dependenciesrestriction = $lesson->get_dependencies_restriction_status()) { // Check for dependencies. + echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('completethefollowingconditions', 'lesson', format_string($lesson->name))); + echo $lessonoutput->dependancy_errors($dependenciesrestriction->dependentlesson, $dependenciesrestriction->errors); + echo $lessonoutput->footer(); + exit(); } +echo $lessonoutput->header($lesson, $cm); + // print the embedded media html code echo $OUTPUT->box(lesson_get_media_html($lesson, $context)); diff --git a/mod/lesson/tests/external_test.php b/mod/lesson/tests/external_test.php new file mode 100644 index 00000000000..b27dc31619e --- /dev/null +++ b/mod/lesson/tests/external_test.php @@ -0,0 +1,195 @@ +. + +/** + * Lesson module external functions tests + * + * @package mod_lesson + * @category external + * @copyright 2017 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 3.3 + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); +require_once($CFG->dirroot . '/mod/lesson/locallib.php'); + +/** + * Lesson module external functions tests + * + * @package mod_lesson + * @category external + * @copyright 2017 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 3.3 + */ +class mod_lesson_external_testcase extends externallib_advanced_testcase { + + /** + * Set up for every test + */ + public function setUp() { + global $DB; + $this->resetAfterTest(); + $this->setAdminUser(); + + // Setup test data. + $this->course = $this->getDataGenerator()->create_course(); + $this->lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $this->course->id)); + $this->context = context_module::instance($this->lesson->cmid); + $this->cm = get_coursemodule_from_instance('lesson', $this->lesson->id); + + // Create users. + $this->student = self::getDataGenerator()->create_user(); + $this->teacher = self::getDataGenerator()->create_user(); + + // Users enrolments. + $this->studentrole = $DB->get_record('role', array('shortname' => 'student')); + $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); + $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual'); + $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual'); + } + + + /** + * Test test_mod_lesson_get_lessons_by_courses + */ + public function test_mod_lesson_get_lessons_by_courses() { + global $DB; + + // Create additional course. + $course2 = self::getDataGenerator()->create_course(); + + // Second lesson. + $record = new stdClass(); + $record->course = $course2->id; + $lesson2 = self::getDataGenerator()->create_module('lesson', $record); + + // Execute real Moodle enrolment as we'll call unenrol() method on the instance later. + $enrol = enrol_get_plugin('manual'); + $enrolinstances = enrol_get_instances($course2->id, true); + foreach ($enrolinstances as $courseenrolinstance) { + if ($courseenrolinstance->enrol == "manual") { + $instance2 = $courseenrolinstance; + break; + } + } + $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id); + + self::setUser($this->student); + + $returndescription = mod_lesson_external::get_lessons_by_courses_returns(); + + // Create what we expect to be returned when querying the two courses. + // First for the student user. + $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles', 'practice', + 'modattempts', 'usepassword', 'grade', 'custom', 'ongoing', 'usemaxgrade', + 'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback', 'minquestions', + 'maxpages', 'timelimit', 'retake', 'mediafile', 'mediafiles', 'mediaheight', 'mediawidth', + 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif', + 'progressbar'); + + // Add expected coursemodule and data. + $lesson1 = $this->lesson; + $lesson1->coursemodule = $lesson1->cmid; + $lesson1->introformat = 1; + $lesson1->section = 0; + $lesson1->visible = true; + $lesson1->groupmode = 0; + $lesson1->groupingid = 0; + $lesson1->introfiles = []; + $lesson1->mediafiles = []; + + $lesson2->coursemodule = $lesson2->cmid; + $lesson2->introformat = 1; + $lesson2->section = 0; + $lesson2->visible = true; + $lesson2->groupmode = 0; + $lesson2->groupingid = 0; + $lesson2->introfiles = []; + $lesson2->mediafiles = []; + + foreach ($expectedfields as $field) { + $expected1[$field] = $lesson1->{$field}; + $expected2[$field] = $lesson2->{$field}; + } + + $expectedlessons = array($expected2, $expected1); + + // Call the external function passing course ids. + $result = mod_lesson_external::get_lessons_by_courses(array($course2->id, $this->course->id)); + $result = external_api::clean_returnvalue($returndescription, $result); + + $this->assertEquals($expectedlessons, $result['lessons']); + $this->assertCount(0, $result['warnings']); + + // Call the external function without passing course id. + $result = mod_lesson_external::get_lessons_by_courses(); + $result = external_api::clean_returnvalue($returndescription, $result); + $this->assertEquals($expectedlessons, $result['lessons']); + $this->assertCount(0, $result['warnings']); + + // Unenrol user from second course and alter expected lessons. + $enrol->unenrol_user($instance2, $this->student->id); + array_shift($expectedlessons); + + // Call the external function without passing course id. + $result = mod_lesson_external::get_lessons_by_courses(); + $result = external_api::clean_returnvalue($returndescription, $result); + $this->assertEquals($expectedlessons, $result['lessons']); + + // Call for the second course we unenrolled the user from, expected warning. + $result = mod_lesson_external::get_lessons_by_courses(array($course2->id)); + $this->assertCount(1, $result['warnings']); + $this->assertEquals('1', $result['warnings'][0]['warningcode']); + $this->assertEquals($course2->id, $result['warnings'][0]['itemid']); + + // Now, try as a teacher for getting all the additional fields. + self::setUser($this->teacher); + + $additionalfields = array('password', 'dependency', 'conditions', 'activitylink', 'available', 'deadline', + 'timemodified', 'completionendreached', 'completiontimespent'); + + foreach ($additionalfields as $field) { + $expectedlessons[0][$field] = $lesson1->{$field}; + } + + $result = mod_lesson_external::get_lessons_by_courses(); + $result = external_api::clean_returnvalue($returndescription, $result); + $this->assertEquals($expectedlessons, $result['lessons']); + + // Admin also should get all the information. + self::setAdminUser(); + + $result = mod_lesson_external::get_lessons_by_courses(array($this->course->id)); + $result = external_api::clean_returnvalue($returndescription, $result); + $this->assertEquals($expectedlessons, $result['lessons']); + + // Now, add a restriction. + $this->setUser($this->student); + $DB->set_field('lesson', 'usepassword', 1, array('id' => $lesson1->id)); + $DB->set_field('lesson', 'password', 'abc', array('id' => $lesson1->id)); + + $lessons = mod_lesson_external::get_lessons_by_courses(array($this->course->id)); + $lessons = external_api::clean_returnvalue(mod_lesson_external::get_lessons_by_courses_returns(), $lessons); + $this->assertFalse(isset($lessons['lessons'][0]['intro'])); + } + +} diff --git a/mod/lesson/version.php b/mod/lesson/version.php index 3f0759f1c37..4b0e1585426 100644 --- a/mod/lesson/version.php +++ b/mod/lesson/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2016120500; // The current module version (Date: YYYYMMDDXX) +$plugin->version = 2016120501; // 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; diff --git a/mod/lesson/view.php b/mod/lesson/view.php index 36805595d31..0d42a6af069 100644 --- a/mod/lesson/view.php +++ b/mod/lesson/view.php @@ -37,7 +37,7 @@ $backtocourse = optional_param('backtocourse', false, PARAM_RAW); $cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST); $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); -$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST)); +$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST), $cm); require_login($course, false, $cm); @@ -59,8 +59,8 @@ if ($pageid !== null) { $PAGE->set_url($url); $PAGE->force_settings_menu(); -$context = context_module::instance($cm->id); -$canmanage = has_capability('mod/lesson:manage', $context); +$context = $lesson->context; +$canmanage = $lesson->can_manage(); $lessonoutput = $PAGE->get_renderer('mod_lesson'); @@ -70,103 +70,29 @@ if ($userhasgrade && !$lesson->retake) { $reviewmode = true; } -/// Check these for students only TODO: Find a better method for doing this! -/// Check lesson availability -/// Check for password -/// Check dependencies -if (!$canmanage) { - if (!$lesson->is_accessible()) { // Deadline restrictions - echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('notavailable')); - if ($lesson->deadline != 0 && time() > $lesson->deadline) { - echo $lessonoutput->lesson_inaccessible(get_string('lessonclosed', 'lesson', userdate($lesson->deadline))); - } else { - echo $lessonoutput->lesson_inaccessible(get_string('lessonopen', 'lesson', userdate($lesson->available))); - } - echo $lessonoutput->footer(); - exit(); - } else if ($lesson->usepassword && empty($USER->lessonloggedin[$lesson->id])) { // Password protected lesson code - $correctpass = false; - if (!empty($userpassword) && (($lesson->password == md5(trim($userpassword))) || ($lesson->password == trim($userpassword)))) { - require_sesskey(); - // with or without md5 for backward compatibility (MDL-11090) - $correctpass = true; - $USER->lessonloggedin[$lesson->id] = true; - - } else if (isset($lesson->extrapasswords)) { - - // Group overrides may have additional passwords. - foreach ($lesson->extrapasswords as $password) { - if (strcmp($password, md5(trim($userpassword))) === 0 || strcmp($password, trim($userpassword)) === 0) { - require_sesskey(); - $correctpass = true; - $USER->lessonloggedin[$lesson->id] = true; - } - } - } - if (!$correctpass) { - echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('passwordprotectedlesson', 'lesson', format_string($lesson->name))); - echo $lessonoutput->login_prompt($lesson, $userpassword !== ''); - echo $lessonoutput->footer(); - exit(); - } - } else if ($lesson->dependency) { // check for dependencies - if ($dependentlesson = $DB->get_record('lesson', array('id' => $lesson->dependency))) { - // lesson exists, so we can proceed - $conditions = unserialize($lesson->conditions); - // assume false for all - $errors = array(); - - // check for the timespent condition - if ($conditions->timespent) { - $timespent = false; - if ($attempttimes = $DB->get_records('lesson_timer', array("userid"=>$USER->id, "lessonid"=>$dependentlesson->id))) { - // go through all the times and test to see if any of them satisfy the condition - foreach($attempttimes as $attempttime) { - $duration = $attempttime->lessontime - $attempttime->starttime; - if ($conditions->timespent < $duration/60) { - $timespent = true; - } - } - } - if (!$timespent) { - $errors[] = get_string('timespenterror', 'lesson', $conditions->timespent); - } - } - - // check for the gradebetterthan condition - if($conditions->gradebetterthan) { - $gradebetterthan = false; - if ($studentgrades = $DB->get_records('lesson_grades', array("userid"=>$USER->id, "lessonid"=>$dependentlesson->id))) { - // go through all the grades and test to see if any of them satisfy the condition - foreach($studentgrades as $studentgrade) { - if ($studentgrade->grade >= $conditions->gradebetterthan) { - $gradebetterthan = true; - } - } - } - if (!$gradebetterthan) { - $errors[] = get_string('gradebetterthanerror', 'lesson', $conditions->gradebetterthan); - } - } - - // check for the completed condition - if ($conditions->completed) { - if (!$DB->count_records('lesson_grades', array('userid'=>$USER->id, 'lessonid'=>$dependentlesson->id))) { - $errors[] = get_string('completederror', 'lesson'); - } - } - - if (!empty($errors)) { // print out the errors if any - echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('completethefollowingconditions', 'lesson', format_string($lesson->name))); - echo $lessonoutput->dependancy_errors($dependentlesson, $errors); - echo $lessonoutput->footer(); - exit(); - } - } - } +if ($lesson->usepassword && !empty($userpassword)) { + require_sesskey(); } - // this is called if a student leaves during a lesson +// Check these for students only TODO: Find a better method for doing this! +if ($timerestriction = $lesson->get_time_restriction_status()) { // Deadline restrictions. + echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('notavailable')); + echo $lessonoutput->lesson_inaccessible(get_string($timerestriction->reason, 'lesson', userdate($timerestriction->time))); + echo $lessonoutput->footer(); + exit(); +} else if ($passwordrestriction = $lesson->get_password_restriction_status($userpassword)) { // Password protected lesson code. + echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('passwordprotectedlesson', 'lesson', format_string($lesson->name))); + echo $lessonoutput->login_prompt($lesson, $userpassword !== ''); + echo $lessonoutput->footer(); + exit(); +} else if ($dependenciesrestriction = $lesson->get_dependencies_restriction_status()) { // Check for dependencies. + echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('completethefollowingconditions', 'lesson', format_string($lesson->name))); + echo $lessonoutput->dependancy_errors($dependenciesrestriction->dependentlesson, $dependenciesrestriction->errors); + echo $lessonoutput->footer(); + exit(); +} + +// This is called if a student leaves during a lesson. if ($pageid == LESSON_UNSEENBRANCHPAGE) { $pageid = lesson_unseen_question_jump($lesson, $USER->id, $pageid); }