var scope = this;
var value = ta.get('value');
if (value && value != M.util.get_string('addcomment', 'moodle')) {
+ ta.set('disabled', true);
+ ta.setStyles({
+ 'backgroundImage': 'url(' + M.util.image_url('i/loading_small', 'core') + ')',
+ 'backgroundRepeat': 'no-repeat',
+ 'backgroundPosition': 'center center'
+ });
var params = {'content': value};
this.request({
action: 'add',
var cid = scope.client_id;
var ta = Y.one('#dlg-content-'+cid);
ta.set('value', '');
+ ta.set('disabled', false);
+ ta.setStyle('backgroundImage', 'none');
scope.toggle_textarea(false);
var container = Y.one('#comment-list-'+cid);
var result = scope.render([obj], true);
* @param string $withcapability
* @param int $groupid 0 means ignore groups, any other value limits the result by group id
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
+ * @param bool $onlysuspended inverse of onlyactive, consider only suspended enrolments
* @return array list($sql, $params)
*/
-function get_enrolled_sql(context $context, $withcapability = '', $groupid = 0, $onlyactive = false) {
+function get_enrolled_sql(context $context, $withcapability = '', $groupid = 0, $onlyactive = false, $onlysuspended = false) {
global $DB, $CFG;
// use unique prefix just in case somebody makes some SQL magic with the result
$isfrontpage = ($coursecontext->instanceid == SITEID);
+ if ($onlyactive && $onlysuspended) {
+ throw new coding_exception("Both onlyactive and onlysuspended are set, this is probably not what you want!");
+ }
+ if ($isfrontpage && $onlysuspended) {
+ throw new coding_exception("onlysuspended is not supported on frontpage; please add your own early-exit!");
+ }
+
$joins = array();
$wheres = array();
$params = array();
if ($isfrontpage) {
// all users are "enrolled" on the frontpage
} else {
- $joins[] = "JOIN {user_enrolments} {$prefix}ue ON {$prefix}ue.userid = {$prefix}u.id";
- $joins[] = "JOIN {enrol} {$prefix}e ON ({$prefix}e.id = {$prefix}ue.enrolid AND {$prefix}e.courseid = :{$prefix}courseid)";
+ $where1 = "{$prefix}ue.status = :{$prefix}active AND {$prefix}e.status = :{$prefix}enabled";
+ $where2 = "{$prefix}ue.timestart < :{$prefix}now1 AND ({$prefix}ue.timeend = 0 OR {$prefix}ue.timeend > :{$prefix}now2)";
+ $ejoin = "JOIN {enrol} {$prefix}e ON ({$prefix}e.id = {$prefix}ue.enrolid AND {$prefix}e.courseid = :{$prefix}courseid)";
$params[$prefix.'courseid'] = $coursecontext->instanceid;
- if ($onlyactive) {
- $wheres[] = "{$prefix}ue.status = :{$prefix}active AND {$prefix}e.status = :{$prefix}enabled";
- $wheres[] = "{$prefix}ue.timestart < :{$prefix}now1 AND ({$prefix}ue.timeend = 0 OR {$prefix}ue.timeend > :{$prefix}now2)";
+ if (!$onlysuspended) {
+ $joins[] = "JOIN {user_enrolments} {$prefix}ue ON {$prefix}ue.userid = {$prefix}u.id";
+ $joins[] = $ejoin;
+ if ($onlyactive) {
+ $wheres[] = "$where1 AND $where2";
+ }
+ } else {
+ // Suspended only where there is enrolment but ALL are suspended.
+ // Consider multiple enrols where one is not suspended or plain role_assign.
+ $enrolselect = "SELECT DISTINCT {$prefix}ue.userid FROM {user_enrolments} {$prefix}ue $ejoin WHERE $where1 AND $where2";
+ $joins[] = "JOIN {user_enrolments} {$prefix}ue1 ON {$prefix}ue1.userid = {$prefix}u.id";
+ $joins[] = "JOIN {enrol} {$prefix}e1 ON ({$prefix}e1.id = {$prefix}ue1.enrolid AND {$prefix}e1.courseid = :{$prefix}_e1_courseid)";
+ $params["{$prefix}_e1_courseid"] = $coursecontext->instanceid;
+ $wheres[] = "{$prefix}u.id NOT IN ($enrolselect)";
+ }
+
+ if ($onlyactive || $onlysuspended) {
$now = round(time(), -2); // rounding helps caching in DB
$params = array_merge($params, array($prefix.'enabled'=>ENROL_INSTANCE_ENABLED,
$prefix.'active'=>ENROL_USER_ACTIVE,
function get_suspended_userids(context $context, $usecache = false) {
global $DB;
- // Check the cache first for performance reasons if enabled.
if ($usecache) {
$cache = cache::make('core', 'suspended_userids');
$susers = $cache->get($context->id);
}
}
- // Get all enrolled users.
- list($sql, $params) = get_enrolled_sql($context);
- $users = $DB->get_records_sql($sql, $params);
-
- // Get active enrolled users.
- list($sql, $params) = get_enrolled_sql($context, null, null, true);
- $activeusers = $DB->get_records_sql($sql, $params);
-
+ $coursecontext = $context->get_course_context();
$susers = array();
- if (sizeof($activeusers) != sizeof($users)) {
- foreach ($users as $userid => $user) {
- if (!array_key_exists($userid, $activeusers)) {
- $susers[$userid] = $userid;
- }
- }
+
+ // Front page users are always enrolled, so suspended list is empty.
+ if ($coursecontext->instanceid != SITEID) {
+ list($sql, $params) = get_enrolled_sql($context, null, null, false, true);
+ $susers = $DB->get_fieldset_sql($sql, $params);
+ $susers = array_combine($susers, $susers);
}
// Cache results for the remainder of this request.
$cache->set($context->id, $susers);
}
- // Return.
return $susers;
}
'capabilities' => '',
),
+ 'core_user_view_user_list' => array(
+ 'classname' => 'core_user_external',
+ 'methodname' => 'view_user_list',
+ 'classpath' => 'user/externallib.php',
+ 'description' => 'Simulates the web-interface view of user/index.php (triggering events).',
+ 'type' => 'write',
+ 'capabilities' => 'moodle/course:viewparticipants',
+ ),
+
// === enrol related functions ===
'core_enrol_get_enrolled_users_with_capability' => array(
'core_completion_get_activities_completion_status',
'core_notes_get_course_notes',
'core_completion_get_course_completion_status',
+ 'core_user_view_user_list',
),
'enabled' => 0,
'restrictedusers' => 0,
}
}
+ /**
+ * Test that enrolled users SQL does not return any values for users in
+ * other courses.
+ */
+ public function test_get_enrolled_sql_different_course() {
+ global $DB;
+
+ $this->resetAfterTest();
+
+ $course = $this->getDataGenerator()->create_course();
+ $context = context_course::instance($course->id);
+ $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+ $user = $this->getDataGenerator()->create_user();
+
+ // This user should not appear anywhere, we're not interested in that context.
+ $course2 = $this->getDataGenerator()->create_course();
+ $this->getDataGenerator()->enrol_user($user->id, $course2->id, $student->id);
+
+ $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
+ $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
+ $suspended = get_suspended_userids($context);
+
+ $this->assertFalse(isset($enrolled[$user->id]));
+ $this->assertFalse(isset($active[$user->id]));
+ $this->assertFalse(isset($suspended[$user->id]));
+ $this->assertCount(0, $enrolled);
+ $this->assertCount(0, $active);
+ $this->assertCount(0, $suspended);
+ }
+
+ /**
+ * Test that enrolled users SQL does not return any values for role
+ * assignments without an enrolment.
+ */
+ public function test_get_enrolled_sql_role_only() {
+ global $DB;
+
+ $this->resetAfterTest();
+
+ $course = $this->getDataGenerator()->create_course();
+ $context = context_course::instance($course->id);
+ $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+ $user = $this->getDataGenerator()->create_user();
+
+ // Role assignment is not the same as course enrollment.
+ role_assign($student->id, $user->id, $context->id);
+
+ $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
+ $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
+ $suspended = get_suspended_userids($context);
+
+ $this->assertFalse(isset($enrolled[$user->id]));
+ $this->assertFalse(isset($active[$user->id]));
+ $this->assertFalse(isset($suspended[$user->id]));
+ $this->assertCount(0, $enrolled);
+ $this->assertCount(0, $active);
+ $this->assertCount(0, $suspended);
+ }
+
+ /**
+ * Test that multiple enrolments for the same user are counted correctly.
+ */
+ public function test_get_enrolled_sql_multiple_enrolments() {
+ global $DB;
+
+ $this->resetAfterTest();
+
+ $course = $this->getDataGenerator()->create_course();
+ $context = context_course::instance($course->id);
+ $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+ $user = $this->getDataGenerator()->create_user();
+
+ // Add a suspended enrol.
+ $selfinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'self'));
+ $selfplugin = enrol_get_plugin('self');
+ $selfplugin->update_status($selfinstance, ENROL_INSTANCE_ENABLED);
+ $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'self', 0, 0, ENROL_USER_SUSPENDED);
+
+ // Should be enrolled, but not active - user is suspended.
+ $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
+ $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
+ $suspended = get_suspended_userids($context);
+
+ $this->assertTrue(isset($enrolled[$user->id]));
+ $this->assertFalse(isset($active[$user->id]));
+ $this->assertTrue(isset($suspended[$user->id]));
+ $this->assertCount(1, $enrolled);
+ $this->assertCount(0, $active);
+ $this->assertCount(1, $suspended);
+
+ // Add an active enrol for the user. Any active enrol makes them enrolled.
+ $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id);
+
+ // User should be active now.
+ $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
+ $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
+ $suspended = get_suspended_userids($context);
+
+ $this->assertTrue(isset($enrolled[$user->id]));
+ $this->assertTrue(isset($active[$user->id]));
+ $this->assertFalse(isset($suspended[$user->id]));
+ $this->assertCount(1, $enrolled);
+ $this->assertCount(1, $active);
+ $this->assertCount(0, $suspended);
+
+ }
+
+ public function get_enrolled_sql_provider() {
+ return array(
+ array(
+ // Two users who are enrolled.
+ 'users' => array(
+ array(
+ 'enrolled' => true,
+ 'active' => true,
+ ),
+ array(
+ 'enrolled' => true,
+ 'active' => true,
+ ),
+ ),
+ 'counts' => array(
+ 'enrolled' => 2,
+ 'active' => 2,
+ 'suspended' => 0,
+ ),
+ ),
+ array(
+ // A user who is suspended.
+ 'users' => array(
+ array(
+ 'status' => ENROL_USER_SUSPENDED,
+ 'enrolled' => true,
+ 'suspended' => true,
+ ),
+ ),
+ 'counts' => array(
+ 'enrolled' => 1,
+ 'active' => 0,
+ 'suspended' => 1,
+ ),
+ ),
+ array(
+ // One of each.
+ 'users' => array(
+ array(
+ 'enrolled' => true,
+ 'active' => true,
+ ),
+ array(
+ 'status' => ENROL_USER_SUSPENDED,
+ 'enrolled' => true,
+ 'suspended' => true,
+ ),
+ ),
+ 'counts' => array(
+ 'enrolled' => 2,
+ 'active' => 1,
+ 'suspended' => 1,
+ ),
+ ),
+ array(
+ // One user who is not yet enrolled.
+ 'users' => array(
+ array(
+ 'timestart' => DAYSECS,
+ 'enrolled' => true,
+ 'active' => false,
+ 'suspended' => true,
+ ),
+ ),
+ 'counts' => array(
+ 'enrolled' => 1,
+ 'active' => 0,
+ 'suspended' => 1,
+ ),
+ ),
+ array(
+ // One user who is no longer enrolled
+ 'users' => array(
+ array(
+ 'timeend' => -DAYSECS,
+ 'enrolled' => true,
+ 'active' => false,
+ 'suspended' => true,
+ ),
+ ),
+ 'counts' => array(
+ 'enrolled' => 1,
+ 'active' => 0,
+ 'suspended' => 1,
+ ),
+ ),
+ array(
+ // One user who is not yet enrolled, and one who is no longer enrolled.
+ 'users' => array(
+ array(
+ 'timeend' => -DAYSECS,
+ 'enrolled' => true,
+ 'active' => false,
+ 'suspended' => true,
+ ),
+ array(
+ 'timestart' => DAYSECS,
+ 'enrolled' => true,
+ 'active' => false,
+ 'suspended' => true,
+ ),
+ ),
+ 'counts' => array(
+ 'enrolled' => 2,
+ 'active' => 0,
+ 'suspended' => 2,
+ ),
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider get_enrolled_sql_provider
+ */
+ public function test_get_enrolled_sql_course($users, $counts) {
+ global $DB;
+
+ $this->resetAfterTest();
+
+ $course = $this->getDataGenerator()->create_course();
+ $context = context_course::instance($course->id);
+ $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+ $createdusers = array();
+
+ foreach ($users as &$userdata) {
+ $user = $this->getDataGenerator()->create_user();
+ $userdata['id'] = $user->id;
+
+ $timestart = 0;
+ $timeend = 0;
+ $status = null;
+ if (isset($userdata['timestart'])) {
+ $timestart = time() + $userdata['timestart'];
+ }
+ if (isset($userdata['timeend'])) {
+ $timeend = time() + $userdata['timeend'];
+ }
+ if (isset($userdata['status'])) {
+ $status = $userdata['status'];
+ }
+
+ // Enrol the user in the course.
+ $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'manual', $timestart, $timeend, $status);
+ }
+
+ // After all users have been enroled, check expectations.
+ $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
+ $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
+ $suspended = get_suspended_userids($context);
+
+ foreach ($users as $userdata) {
+ if (isset($userdata['enrolled']) && $userdata['enrolled']) {
+ $this->assertTrue(isset($enrolled[$userdata['id']]));
+ } else {
+ $this->assertFalse(isset($enrolled[$userdata['id']]));
+ }
+
+ if (isset($userdata['active']) && $userdata['active']) {
+ $this->assertTrue(isset($active[$userdata['id']]));
+ } else {
+ $this->assertFalse(isset($active[$userdata['id']]));
+ }
+
+ if (isset($userdata['suspended']) && $userdata['suspended']) {
+ $this->assertTrue(isset($suspended[$userdata['id']]));
+ } else {
+ $this->assertFalse(isset($suspended[$userdata['id']]));
+ }
+ }
+
+ $this->assertCount($counts['enrolled'], $enrolled);
+ $this->assertCount($counts['active'], $active);
+ $this->assertCount($counts['suspended'], $suspended);
+ }
+
/**
* A small functional test of permission evaluations.
*/
* This is executed when we reach the closing </MOD> tag of our 'lesson' path
*/
public function on_lesson_end() {
+ // Append empty <overrides> subpath element.
+ $this->write_xml('overrides', array());
+
// finish writing lesson.xml
$this->xmlwriter->end_tag('lesson');
$this->xmlwriter->end_tag('activity');
*
* This is the "graphical" structure of the lesson module:
*
- * lesson ------------>---------------|-------------->-----------|------------->------------|
- * (CL,pk->id) | | |
- * | | | |
- * | lesson_grades lesson_high_scores lesson_timer
- * | (UL, pk->id,fk->lessonid) (UL, pk->id,fk->lessonid) (UL, pk->id,fk->lessonid)
- * | |
- * | |
- * | |
- * | |
- * lesson_pages----------->---------lesson_branch
- * (CL,pk->id,fk->lessonid) (UL, pk->id,fk->pageid)
- * |
- * |
- * |
- * lesson_answers
- * (CL,pk->id,fk->pageid)
- * |
- * |
- * |
- * lesson_attempts
- * (UL,pk->id,fk->answerid)
+ * lesson ---------->-------------|------------>---------|----------->----------|----------->----------|
+ * (CL,pk->id) | | | |
+ * | | | | |
+ * | lesson_grades lesson_high_scores lesson_timer lesson_overrides
+ * | (UL, pk->id,fk->lessonid) (UL, pk->id,fk->lessonid) (UL, pk->id,fk->lessonid) (UL, pk->id,fk->lessonid)
+ * | |
+ * | |
+ * | |
+ * | |
+ * lesson_pages-------->-------lesson_branch
+ * (CL,pk->id,fk->lessonid) (UL, pk->id,fk->pageid)
+ * |
+ * |
+ * |
+ * lesson_answers
+ * (CL,pk->id,fk->pageid)
+ * |
+ * |
+ * |
+ * lesson_attempts
+ * (UL,pk->id,fk->answerid)
*
* Meaning: pk->primary key field of the table
* fk->foreign key to link with parent
'userid', 'starttime', 'lessontime', 'completed'
));
+ $overrides = new backup_nested_element('overrides');
+ $override = new backup_nested_element('override', array('id'), array(
+ 'groupid', 'userid', 'available', 'deadline', 'timelimit',
+ 'review', 'maxattempts', 'retake', 'password'));
+
// Now that we have all of the elements created we've got to put them
// together correctly.
$lesson->add_child($pages);
$highscores->add_child($highscore);
$lesson->add_child($timers);
$timers->add_child($timer);
+ $lesson->add_child($overrides);
+ $overrides->add_child($override);
// Set the source table for the elements that aren't reliant on the user
// at this point (lesson, lesson_pages, lesson_answers)
// We use SQL here as answers must be ordered by id so that the restore gets them in the right order
$answer->set_source_table('lesson_answers', array('pageid' => backup::VAR_PARENTID), 'id ASC');
+ // Lesson overrides to backup are different depending of user info.
+ $overrideparams = array('lessonid' => backup::VAR_PARENTID);
+
// Check if we are also backing up user information
if ($this->get_setting_value('userinfo')) {
// Set the source table for elements that are reliant on the user
$grade->set_source_table('lesson_grades', array('lessonid'=>backup::VAR_PARENTID));
$highscore->set_source_table('lesson_high_scores', array('lessonid' => backup::VAR_PARENTID));
$timer->set_source_table('lesson_timer', array('lessonid' => backup::VAR_PARENTID));
+ $overrideparams['userid'] = backup_helper::is_sqlparam(null); // Without userinfo, skip user overrides.
}
+ $override->set_source_table('lesson_overrides', $overrideparams);
// Annotate the user id's where required.
$attempt->annotate_ids('user', 'userid');
$grade->annotate_ids('user', 'userid');
$highscore->annotate_ids('user', 'userid');
$timer->annotate_ids('user', 'userid');
+ $override->annotate_ids('user', 'userid');
+ $override->annotate_ids('group', 'groupid');
// Annotate the file areas in user by the lesson module.
$lesson->annotate_files('mod_lesson', 'intro', null);
$paths[] = new restore_path_element('lesson_branch', '/activity/lesson/pages/page/branches/branch');
$paths[] = new restore_path_element('lesson_highscore', '/activity/lesson/highscores/highscore');
$paths[] = new restore_path_element('lesson_timer', '/activity/lesson/timers/timer');
+ $paths[] = new restore_path_element('lesson_override', '/activity/lesson/overrides/override');
}
// Return the paths wrapped into standard activity structure
$newitemid = $DB->insert_record('lesson_timer', $data);
}
+ /**
+ * Process a lesson override restore
+ * @param object $data The data in object form
+ * @return void
+ */
+ protected function process_lesson_override($data) {
+ global $DB;
+
+ $data = (object)$data;
+ $oldid = $data->id;
+
+ // Based on userinfo, we'll restore user overides or no.
+ $userinfo = $this->get_setting_value('userinfo');
+
+ // Skip user overrides if we are not restoring userinfo.
+ if (!$userinfo && !is_null($data->userid)) {
+ return;
+ }
+
+ $data->lessonid = $this->get_new_parentid('lesson');
+
+ $data->userid = $this->get_mappingid('user', $data->userid);
+ $data->groupid = $this->get_mappingid('group', $data->groupid);
+
+ $data->available = $this->apply_date_offset($data->available);
+ $data->deadline = $this->apply_date_offset($data->deadline);
+
+ $newitemid = $DB->insert_record('lesson_overrides', $data);
+
+ // Add mapping, restore of logs needs it.
+ $this->set_mapping('lesson_override', $oldid, $newitemid);
+ }
+
protected function after_execute() {
global $DB;
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * The mod_lesson group override created event.
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace mod_lesson\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The mod_lesson group override created event class.
+ *
+ * @property-read array $other {
+ * Extra information about event.
+ *
+ * - int lessonid: the id of the lesson.
+ * - int groupid: the id of the group.
+ * }
+ *
+ * @package mod_lesson
+ * @since Moodle 2.9
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class group_override_created extends \core\event\base {
+
+ /**
+ * Init method.
+ */
+ protected function init() {
+ $this->data['objecttable'] = 'lesson_overrides';
+ $this->data['crud'] = 'c';
+ $this->data['edulevel'] = self::LEVEL_TEACHING;
+ }
+
+ /**
+ * Returns localised general event name.
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return get_string('eventoverridecreated', 'mod_lesson');
+ }
+
+ /**
+ * Returns description of what happened.
+ *
+ * @return string
+ */
+ public function get_description() {
+ return "The user with id '$this->userid' created the override with id '$this->objectid' for the lesson with " .
+ "course module id '$this->contextinstanceid' for the group with id '{$this->other['groupid']}'.";
+ }
+
+ /**
+ * Returns relevant URL.
+ *
+ * @return \moodle_url
+ */
+ public function get_url() {
+ return new \moodle_url('/mod/lesson/overrideedit.php', array('id' => $this->objectid));
+ }
+
+ /**
+ * Custom validation.
+ *
+ * @throws \coding_exception
+ * @return void
+ */
+ protected function validate_data() {
+ parent::validate_data();
+
+ if (!isset($this->other['lessonid'])) {
+ throw new \coding_exception('The \'lessonid\' value must be set in other.');
+ }
+
+ if (!isset($this->other['groupid'])) {
+ throw new \coding_exception('The \'groupid\' value must be set in other.');
+ }
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * The mod_lesson group override deleted event.
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_lesson\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The mod_lesson group override deleted event class.
+ *
+ * @property-read array $other {
+ * Extra information about event.
+ *
+ * - int lessonid: the id of the lesson.
+ * - int groupid: the id of the group.
+ * }
+ *
+ * @package mod_lesson
+ * @since Moodle 2.9
+ * @copyright 2015 Jean-Michel vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class group_override_deleted extends \core\event\base {
+
+ /**
+ * Init method.
+ */
+ protected function init() {
+ $this->data['objecttable'] = 'lesson_overrides';
+ $this->data['crud'] = 'd';
+ $this->data['edulevel'] = self::LEVEL_TEACHING;
+ }
+
+ /**
+ * Returns localised general event name.
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return get_string('eventoverridedeleted', 'mod_lesson');
+ }
+
+ /**
+ * Returns description of what happened.
+ *
+ * @return string
+ */
+ public function get_description() {
+ return "The user with id '$this->userid' deleted the override with id '$this->objectid' for the lesson with " .
+ "course module id '$this->contextinstanceid' for the group with id '{$this->other['groupid']}'.";
+ }
+
+ /**
+ * Returns relevant URL.
+ *
+ * @return \moodle_url
+ */
+ public function get_url() {
+ return new \moodle_url('/mod/lesson/overrides.php', array('cmid' => $this->contextinstanceid));
+ }
+
+ /**
+ * Custom validation.
+ *
+ * @throws \coding_exception
+ * @return void
+ */
+ protected function validate_data() {
+ parent::validate_data();
+
+ if (!isset($this->other['lessonid'])) {
+ throw new \coding_exception('The \'lessonid\' value must be set in other.');
+ }
+
+ if (!isset($this->other['groupid'])) {
+ throw new \coding_exception('The \'groupid\' value must be set in other.');
+ }
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * The mod_lesson group override updated event.
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_lesson\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The mod_lesson group override updated event class.
+ *
+ * @property-read array $other {
+ * Extra information about event.
+ *
+ * - int lessonid: the id of the lesson.
+ * - int groupid: the id of the group.
+ * }
+ *
+ * @package mod_lesson
+ * @since Moodle 2.9
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class group_override_updated extends \core\event\base {
+
+ /**
+ * Init method.
+ */
+ protected function init() {
+ $this->data['objecttable'] = 'lesson_overrides';
+ $this->data['crud'] = 'u';
+ $this->data['edulevel'] = self::LEVEL_TEACHING;
+ }
+
+ /**
+ * Returns localised general event name.
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return get_string('eventoverrideupdated', 'mod_lesson');
+ }
+
+ /**
+ * Returns description of what happened.
+ *
+ * @return string
+ */
+ public function get_description() {
+ return "The user with id '$this->userid' updated the override with id '$this->objectid' for the lesson with " .
+ "course module id '$this->contextinstanceid' for the group with id '{$this->other['groupid']}'.";
+ }
+
+ /**
+ * Returns relevant URL.
+ *
+ * @return \moodle_url
+ */
+ public function get_url() {
+ return new \moodle_url('/mod/lesson/overrideedit.php', array('id' => $this->objectid));
+ }
+
+ /**
+ * Custom validation.
+ *
+ * @throws \coding_exception
+ * @return void
+ */
+ protected function validate_data() {
+ parent::validate_data();
+
+ if (!isset($this->other['lessonid'])) {
+ throw new \coding_exception('The \'lessonid\' value must be set in other.');
+ }
+
+ if (!isset($this->other['groupid'])) {
+ throw new \coding_exception('The \'groupid\' value must be set in other.');
+ }
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * The mod_lesson user override created event.
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_lesson\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The mod_lesson user override created event class.
+ *
+ * @property-read array $other {
+ * Extra information about event.
+ *
+ * - int lessonid: the id of the lesson.
+ * }
+ *
+ * @package mod_lesson
+ * @since Moodle 2.9
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class user_override_created extends \core\event\base {
+
+ /**
+ * Init method.
+ */
+ protected function init() {
+ $this->data['objecttable'] = 'lesson_overrides';
+ $this->data['crud'] = 'c';
+ $this->data['edulevel'] = self::LEVEL_TEACHING;
+ }
+
+ /**
+ * Returns localised general event name.
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return get_string('eventoverridecreated', 'mod_lesson');
+ }
+
+ /**
+ * Returns description of what happened.
+ *
+ * @return string
+ */
+ public function get_description() {
+ return "The user with id '$this->userid' created the override with id '$this->objectid' for the lesson with " .
+ "course module id '$this->contextinstanceid' for the user with id '{$this->relateduserid}'.";
+ }
+
+ /**
+ * Returns relevant URL.
+ *
+ * @return \moodle_url
+ */
+ public function get_url() {
+ return new \moodle_url('/mod/lesson/overrideedit.php', array('id' => $this->objectid));
+ }
+
+ /**
+ * Custom validation.
+ *
+ * @throws \coding_exception
+ * @return void
+ */
+ protected function validate_data() {
+ parent::validate_data();
+
+ if (!isset($this->relateduserid)) {
+ throw new \coding_exception('The \'relateduserid\' must be set.');
+ }
+
+ if (!isset($this->other['lessonid'])) {
+ throw new \coding_exception('The \'lessonid\' value must be set in other.');
+ }
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * The mod_lesson user override deleted event.
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_lesson\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The mod_lesson user override deleted event class.
+ *
+ * @property-read array $other {
+ * Extra information about event.
+ *
+ * - int lessonid: the id of the lesson.
+ * }
+ *
+ * @package mod_lesson
+ * @since Moodle 2.9
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class user_override_deleted extends \core\event\base {
+
+ /**
+ * Init method.
+ */
+ protected function init() {
+ $this->data['objecttable'] = 'lesson_overrides';
+ $this->data['crud'] = 'd';
+ $this->data['edulevel'] = self::LEVEL_TEACHING;
+ }
+
+ /**
+ * Returns localised general event name.
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return get_string('eventoverridedeleted', 'mod_lesson');
+ }
+
+ /**
+ * Returns description of what happened.
+ *
+ * @return string
+ */
+ public function get_description() {
+ return "The user with id '$this->userid' deleted the override with id '$this->objectid' for the lesson with " .
+ "course module id '$this->contextinstanceid' for the user with id '{$this->relateduserid}'.";
+ }
+
+ /**
+ * Returns relevant URL.
+ *
+ * @return \moodle_url
+ */
+ public function get_url() {
+ return new \moodle_url('/mod/lesson/overrides.php', array('cmid' => $this->contextinstanceid));
+ }
+
+ /**
+ * Custom validation.
+ *
+ * @throws \coding_exception
+ * @return void
+ */
+ protected function validate_data() {
+ parent::validate_data();
+
+ if (!isset($this->relateduserid)) {
+ throw new \coding_exception('The \'relateduserid\' must be set.');
+ }
+
+ if (!isset($this->other['lessonid'])) {
+ throw new \coding_exception('The \'lessonid\' value must be set in other.');
+ }
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * The mod_lesson user override updated event.
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace mod_lesson\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The mod_lesson user override updated event class.
+ *
+ * @property-read array $other {
+ * Extra information about event.
+ *
+ * - int lessonid: the id of the lesson.
+ * }
+ *
+ * @package mod_lesson
+ * @since Moodle 2.9
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class user_override_updated extends \core\event\base {
+
+ /**
+ * Init method.
+ */
+ protected function init() {
+ $this->data['objecttable'] = 'lesson_overrides';
+ $this->data['crud'] = 'u';
+ $this->data['edulevel'] = self::LEVEL_TEACHING;
+ }
+
+ /**
+ * Returns localised general event name.
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return get_string('eventoverrideupdated', 'mod_lesson');
+ }
+
+ /**
+ * Returns description of what happened.
+ *
+ * @return string
+ */
+ public function get_description() {
+ return "The user with id '$this->userid' updated the override with id '$this->objectid' for the lesson with " .
+ "course module id '$this->contextinstanceid' for the user with id '{$this->relateduserid}'.";
+ }
+
+ /**
+ * Returns relevant URL.
+ *
+ * @return \moodle_url
+ */
+ public function get_url() {
+ return new \moodle_url('/mod/lesson/overrideedit.php', array('id' => $this->objectid));
+ }
+
+ /**
+ * Custom validation.
+ *
+ * @throws \coding_exception
+ * @return void
+ */
+ protected function validate_data() {
+ parent::validate_data();
+
+ if (!isset($this->relateduserid)) {
+ throw new \coding_exception('The \'relateduserid\' must be set.');
+ }
+
+ if (!isset($this->other['lessonid'])) {
+ throw new \coding_exception('The \'lessonid\' value must be set in other.');
+ }
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Group observers.
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace mod_lesson;
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot . '/mod/lesson/locallib.php');
+
+/**
+ * Group observers class.
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class group_observers {
+
+ /**
+ * Flag whether a course reset is in progress or not.
+ *
+ * @var int The course ID.
+ */
+ protected static $resetinprogress = false;
+
+ /**
+ * A course reset has started.
+ *
+ * @param \core\event\base $event The event.
+ * @return void
+ */
+ public static function course_reset_started($event) {
+ self::$resetinprogress = $event->courseid;
+ }
+
+ /**
+ * A course reset has ended.
+ *
+ * @param \core\event\base $event The event.
+ * @return void
+ */
+ public static function course_reset_ended($event) {
+ if (!empty(self::$resetinprogress)) {
+ if (!empty($event->other['reset_options']['reset_groups_remove'])) {
+ lesson_process_group_deleted_in_course($event->courseid);
+ }
+ }
+
+ self::$resetinprogress = null;
+ }
+
+ /**
+ * A group was deleted.
+ *
+ * @param \core\event\base $event The event.
+ * @return void
+ */
+ public static function group_deleted($event) {
+ if (!empty(self::$resetinprogress)) {
+ // We will take care of that once the course reset ends.
+ return;
+ }
+ lesson_process_group_deleted_in_course($event->courseid, $event->objectid);
+ }
+}
require_login($course, false, $cm);
require_sesskey();
+// Apply overrides.
+$lesson->update_effective_access($USER->id);
+
$context = context_module::instance($cm->id);
$canmanage = has_capability('mod/lesson:manage', $context);
$lessonoutput = $PAGE->get_renderer('mod_lesson');
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
- )
+ ),
+
+ // Edit the lesson overrides.
+ 'mod/lesson:manageoverrides' => array(
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_MODULE,
+ 'archetypes' => array(
+ 'editingteacher' => CAP_ALLOW,
+ 'manager' => CAP_ALLOW
+ )
+ ),
);
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Add event handlers for the lesson
+ *
+ * @package mod_lesson
+ * @category event
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+$observers = array(
+
+ array(
+ 'eventname' => '\core\event\course_reset_started',
+ 'callback' => '\mod_lesson\group_observers::course_reset_started',
+ ),
+ array(
+ 'eventname' => '\core\event\course_reset_ended',
+ 'callback' => '\mod_lesson\group_observers::course_reset_ended',
+ ),
+ array(
+ 'eventname' => '\core\event\group_deleted',
+ 'callback' => '\mod_lesson\group_observers::group_deleted'
+ ),
+);
<INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
</INDEXES>
</TABLE>
+ <TABLE NAME="lesson_overrides" COMMENT="The overrides to lesson settings.">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="lessonid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references lesson.id"/>
+ <FIELD NAME="groupid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Foreign key references groups.id. Can be null if this is a per-user override."/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Foreign key references user.id. Can be null if this is a per-group override."/>
+ <FIELD NAME="available" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time at which students may start attempting this lesson. Can be null, in which case the lesson default is used."/>
+ <FIELD NAME="deadline" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time by which students must have completed their attempt. Can be null, in which case the lesson default is used."/>
+ <FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time limit in seconds. Can be null, in which case the lesson default is used."/>
+ <FIELD NAME="review" TYPE="int" LENGTH="3" NOTNULL="false" SEQUENCE="false"/>
+ <FIELD NAME="maxattempts" TYPE="int" LENGTH="3" NOTNULL="false" SEQUENCE="false"/>
+ <FIELD NAME="retake" TYPE="int" LENGTH="3" NOTNULL="false" SEQUENCE="false"/>
+ <FIELD NAME="password" TYPE="char" LENGTH="32" NOTNULL="false" SEQUENCE="false" COMMENT="Lesson password. Can be null, in which case the lesson default is used."/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ <KEY NAME="lessonid" TYPE="foreign" FIELDS="lessonid" REFTABLE="lesson" REFFIELDS="id"/>
+ <KEY NAME="groupid" TYPE="foreign" FIELDS="groupid" REFTABLE="groups" REFFIELDS="id"/>
+ <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
+ </KEYS>
+ </TABLE>
</TABLES>
</XMLDB>
upgrade_mod_savepoint(true, 2015032700, 'lesson');
}
+ if ($oldversion < 2015033100) {
+
+ // Define table lesson_overrides to be created.
+ $table = new xmldb_table('lesson_overrides');
+
+ // Adding fields to table lesson_overrides.
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('lessonid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+ $table->add_field('groupid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
+ $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
+ $table->add_field('available', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
+ $table->add_field('deadline', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
+ $table->add_field('timelimit', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
+ $table->add_field('review', XMLDB_TYPE_INTEGER, '3', null, null, null, null);
+ $table->add_field('maxattempts', XMLDB_TYPE_INTEGER, '3', null, null, null, null);
+ $table->add_field('retake', XMLDB_TYPE_INTEGER, '3', null, null, null, null);
+ $table->add_field('password', XMLDB_TYPE_CHAR, '32', null, null, null, null);
+
+ // Adding keys to table lesson_overrides.
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+ $table->add_key('lessonid', XMLDB_KEY_FOREIGN, array('lessonid'), 'lesson', array('id'));
+ $table->add_key('groupid', XMLDB_KEY_FOREIGN, array('groupid'), 'groups', array('id'));
+ $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
+
+ // Conditionally launch create table for lesson_overrides.
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+ // Lesson savepoint reached.
+ upgrade_mod_savepoint(true, 2015033100, 'lesson');
+ }
+
return true;
}
$attempt = $DB->get_record('lesson_attempts', array('id' => $attemptid));
$answer = $DB->get_record('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $attempt->pageid));
$user = $DB->get_record('user', array('id' => $attempt->userid));
+ // Apply overrides.
+ $lesson->update_effective_access($user->id);
$scoreoptions = array();
if ($lesson->custom) {
$i = $answer->score;
$string['addedcluster'] = 'Added a cluster';
$string['addedendofcluster'] = 'Added an end of cluster';
$string['addendofcluster'] = 'Add an end of cluster';
+$string['addnewgroupoverride'] = 'Add group override';
+$string['addnewuseroverride'] = 'Add user override';
$string['addpage'] = 'Add a page';
$string['and'] = 'AND';
$string['anchortitle'] = 'Start of main content';
$string['classstats'] = 'Class statistics';
$string['clicktodownload'] = 'Click on the following link to download the file.';
$string['clicktopost'] = 'Click here to post your grade on the High Scores list.';
+$string['closebeforeopen'] = 'Could not update the lesson. You have specified a close date before the open date.';
$string['cluster'] = 'Cluster';
$string['clusterjump'] = 'Unseen question within a cluster';
$string['clustertitle'] = 'Cluster';
$string['detailedstats'] = 'Detailed statistics';
$string['didnotanswerquestion'] = 'Did not answer this question.';
$string['didnotreceivecredit'] = 'Did not receive credit';
+$string['disabled'] = 'Disabled';
$string['displaydefaultfeedback'] = 'Use default feedback';
$string['displaydefaultfeedback_help'] = 'If enabled, when a response is not found for a particular question, the default response of "That\'s the correct answer" or "That\'s the wrong answer" will be shown.';
$string['displayhighscores'] = 'Display high scores';
$string['edit'] = 'Edit';
$string['editingquestionpage'] = 'Editing {$a} question page';
$string['editlessonsettings'] = 'Edit lesson settings';
+$string['editoverride'] = 'Edit override';
$string['editpage'] = 'Edit page contents';
$string['editpagecontent'] = 'Edit page contents';
$string['email'] = 'Email';
$string['emailallgradedessays'] = 'Email ALL graded essays';
$string['emailgradedessays'] = 'Email graded essays';
$string['emailsuccess'] = 'Emails sent successfully';
+$string['enabled'] = 'Enabled';
$string['endofbranch'] = 'End of branch';
$string['endofcluster'] = 'End of cluster';
$string['endofclustertitle'] = 'End of cluster';
$string['eventlessonrestarted'] = 'Lesson restarted';
$string['eventlessonresumed'] = 'Lesson resumed';
$string['eventlessonstarted'] = 'Lesson started';
+$string['eventoverridecreated'] = 'Lesson override created';
+$string['eventoverridedeleted'] = 'Lesson override deleted';
+$string['eventoverrideupdated'] = 'Lesson override updated';
$string['eventpagecreated'] = 'Page created';
$string['eventpagemoved'] = 'Page moved';
$string['eventpageupdated'] = 'Page updated';
$string['gradeessay'] = 'Grade essay questions ({$a->notgradedcount} not graded and {$a->notsentcount} not sent)';
$string['gradeis'] = 'Grade is {$a}';
$string['gradeoptions'] = 'Grade options';
+$string['groupoverrides'] = 'Group overrides';
+$string['groupoverridesdeleted'] = 'Group overrides deleted';
+$string['groupsnone'] = 'There are no groups in this course';
$string['handlingofretakes'] = 'Handling of re-takes';
$string['handlingofretakes_help'] = 'If re-takes are allowed, this setting specifies whether the grade for the lesson is the mean or maximum of all attempts.';
$string['havenotgradedyet'] = 'Have not graded yet.';
$string['importcount'] = 'Importing {$a} questions';
$string['importquestions'] = 'Import questions';
$string['importquestions_help'] = 'This feature enables questions in a variety of formats to be imported via text file.';
+$string['inactiveoverridehelp'] = '* Student does not have the correct group or role to view/attempt the lesson';
$string['insertedpage'] = 'Inserted page';
$string['invalidfile'] = 'Invalid file';
$string['invalidid'] = 'No course module ID or lesson ID were passed';
$string['invalidlessonid'] = 'lesson ID was incorrect';
+$string['invalidoverrideid'] = 'Invalid override id';
$string['invalidpageid'] = 'Invalid page ID';
$string['jump'] = 'Jump';
$string['jumps'] = 'Jumps';
$string['lesson:edit'] = 'Edit a lesson activity';
$string['lessonformating'] = 'Lesson formatting';
$string['lesson:manage'] = 'Manage a lesson activity';
+$string['lesson:manageoverrides'] = 'Manage lesson overrides';
$string['lesson:viewreports'] = 'View lesson reports';
$string['lessonmenu'] = 'Lesson menu';
$string['lessonnotready'] = 'This lesson is not ready to be taken. Please contact your {$a}.';
$string['noanswer'] = 'One or more questions have no answer given. Please go back and submit an answer.';
$string['noattemptrecordsfound'] = 'No attempt records found: no grade given';
$string['nobranchtablefound'] = 'No content page found';
+$string['noclose'] = 'No close date';
$string['nocommentyet'] = 'No comment yet.';
$string['nocoursemods'] = 'No activities found';
$string['nocredit'] = 'No credit';
$string['nohighscores'] = 'No high scores';
$string['nolessonattempts'] = 'No attempts have been made on this lesson.';
$string['nolessonattemptsgroup'] = 'No attempts have been made by {$a} group members on this lesson.';
+$string['none'] = 'None';
$string['nooneansweredcorrectly'] = 'No one answered correctly.';
$string['nooneansweredthisquestion'] = 'No one answered this question.';
$string['nooneenteredthis'] = 'No one entered this.';
$string['noonehasanswered'] = 'No one has answered an essay question yet.';
$string['noonehasansweredgroup'] = 'No one in {$a} has answered an essay question yet.';
$string['noonecheckedthis'] = 'No one checked this.';
+$string['noopen'] = 'No open date';
+$string['nooverridedata'] = 'You must override at least one of the lesson settings.';
$string['noretake'] = 'You are not allowed to retake this lesson.';
$string['normal'] = 'Normal - follow lesson path';
$string['notcompleted'] = 'Not completed';
$string['ordered'] = 'Ordered';
$string['other'] = 'Other';
$string['outof'] = 'Out of {$a}';
+$string['override'] = 'Override';
+$string['overridedeletegroupsure'] = 'Are you sure you want to delete the override for group {$a}?';
+$string['overridedeleteusersure'] = 'Are you sure you want to delete the override for user {$a}?';
+$string['overridegroup'] = 'Override group';
+$string['overridegroupeventname'] = '{$a->lesson} - {$a->group}';
+$string['overrides'] = 'Overrides';
+$string['overrideuser'] = 'Override user';
+$string['overrideusereventname'] = '{$a->lesson} - Override';
$string['overview'] = 'Overview';
$string['overview_help'] = 'A lesson is made up of a number of pages and optionally content pages. A page contains some content and usually ends with a question. Associated with each answer to the question is a jump. The jump can be relative, such as this page or next page, or absolute, specifying any one of the pages in the lesson. A content page is a page containing a set of links to other pages in the lesson, for example a Table of Contents.';
$string['page'] = 'Page: {$a}';
$string['rawgrade'] = 'Raw grade';
$string['receivedcredit'] = 'Received credit';
$string['redisplaypage'] = 'Redisplay page';
+$string['removeallgroupoverrides'] = 'Delete all group overrides';
+$string['removealluseroverrides'] = 'Delete all user overrides';
$string['report'] = 'Report';
$string['reports'] = 'Reports';
$string['response'] = 'Response';
$string['retakesallowed_help'] = 'If enabled, students can attempt the lesson more than once.';
$string['returnto'] = 'Return to {$a}';
$string['returntocourse'] = 'Return to the course';
+$string['reverttodefaults'] = 'Revert to lesson defaults';
$string['review'] = 'Review';
$string['reviewlesson'] = 'Review lesson';
$string['reviewquestionback'] = 'Yes, I\'d like to try again';
$string['reviewquestioncontinue'] = 'No, I just want to go on to the next question';
$string['sanitycheckfailed'] = 'Sanity check failed: This attempt has been deleted';
+$string['save'] = 'Save';
$string['savechanges'] = 'Save changes';
$string['savechangesandeol'] = 'Save all changes and go to the end of the lesson.';
+$string['saveoverrideandstay'] = 'Save and enter another override';
$string['savepage'] = 'Save page';
$string['score'] = 'Score';
$string['score_help'] = 'Score is only used when custom scoring is enabled. Each answer can then be given a numerical point value (positive or negative).';
$string['usemean'] = 'Use mean';
$string['usepassword'] = 'Password protected lesson';
$string['usepassword_help'] = 'If enabled, a password is required in order to access the lesson.';
+$string['useroverrides'] = 'User overrides';
+$string['useroverridesdeleted'] = 'User overrides deleted';
+$string['usersnone'] = 'No students have access to this lesson';
$string['viewgrades'] = 'View grades';
$string['viewhighscores'] = 'View high scores list';
$string['viewreports'] = 'View {$a->attempts} completed {$a->student} attempts';
return true;
}
+/**
+ * This function updates the events associated to the lesson.
+ * If $override is non-zero, then it updates only the events
+ * associated with the specified override.
+ *
+ * @uses LESSON_MAX_EVENT_LENGTH
+ * @param object $lesson the lesson object.
+ * @param object $override (optional) limit to a specific override
+ */
+function lesson_update_events($lesson, $override = null) {
+ global $CFG, $DB;
+
+ require_once($CFG->dirroot . '/calendar/lib.php');
+
+ // Load the old events relating to this lesson.
+ $conds = array('modulename' => 'lesson',
+ 'instance' => $lesson->id);
+ if (!empty($override)) {
+ // Only load events for this override.
+ if (isset($override->userid)) {
+ $conds['userid'] = $override->userid;
+ } else {
+ $conds['groupid'] = $override->groupid;
+ }
+ }
+ $oldevents = $DB->get_records('event', $conds);
+
+ // Now make a todo list of all that needs to be updated.
+ if (empty($override)) {
+ // We are updating the primary settings for the lesson, so we
+ // need to add all the overrides.
+ $overrides = $DB->get_records('lesson_overrides', array('lessonid' => $lesson->id));
+ // As well as the original lesson (empty override).
+ $overrides[] = new stdClass();
+ } else {
+ // Just do the one override.
+ $overrides = array($override);
+ }
+
+ foreach ($overrides as $current) {
+ $groupid = isset($current->groupid) ? $current->groupid : 0;
+ $userid = isset($current->userid) ? $current->userid : 0;
+ $available = isset($current->available) ? $current->available : $lesson->available;
+ $deadline = isset($current->deadline) ? $current->deadline : $lesson->deadline;
+
+ // Only add open/close events for an override if they differ from the lesson default.
+ $addopen = empty($current->id) || !empty($current->available);
+ $addclose = empty($current->id) || !empty($current->deadline);
+
+ if (!empty($lesson->coursemodule)) {
+ $cmid = $lesson->coursemodule;
+ } else {
+ $cmid = get_coursemodule_from_instance('lesson', $lesson->id, $lesson->course)->id;
+ }
+
+ $event = new stdClass();
+ $event->description = format_module_intro('lesson', $lesson, $cmid);
+ // Events module won't show user events when the courseid is nonzero.
+ $event->courseid = ($userid) ? 0 : $lesson->course;
+ $event->groupid = $groupid;
+ $event->userid = $userid;
+ $event->modulename = 'lesson';
+ $event->instance = $lesson->id;
+ $event->timestart = $available;
+ $event->timeduration = max($deadline - $available, 0);
+ $event->visible = instance_is_visible('lesson', $lesson);
+ $event->eventtype = 'open';
+
+ // Determine the event name.
+ if ($groupid) {
+ $params = new stdClass();
+ $params->lesson = $lesson->name;
+ $params->group = groups_get_group_name($groupid);
+ if ($params->group === false) {
+ // Group doesn't exist, just skip it.
+ continue;
+ }
+ $eventname = get_string('overridegroupeventname', 'lesson', $params);
+ } else if ($userid) {
+ $params = new stdClass();
+ $params->lesson = $lesson->name;
+ $eventname = get_string('overrideusereventname', 'lesson', $params);
+ } else {
+ $eventname = $lesson->name;
+ }
+ if ($addopen or $addclose) {
+ if ($deadline and $available and $event->timeduration <= LESSON_MAX_EVENT_LENGTH) {
+ // Single event for the whole lesson.
+ if ($oldevent = array_shift($oldevents)) {
+ $event->id = $oldevent->id;
+ } else {
+ unset($event->id);
+ }
+ $event->name = $eventname;
+ // The method calendar_event::create will reuse a db record if the id field is set.
+ calendar_event::create($event);
+ } else {
+ // Separate start and end events.
+ $event->timeduration = 0;
+ if ($available && $addopen) {
+ if ($oldevent = array_shift($oldevents)) {
+ $event->id = $oldevent->id;
+ } else {
+ unset($event->id);
+ }
+ $event->name = $eventname.' ('.get_string('lessonopens', 'lesson').')';
+ // The method calendar_event::create will reuse a db record if the id field is set.
+ calendar_event::create($event);
+ }
+ if ($deadline && $addclose) {
+ if ($oldevent = array_shift($oldevents)) {
+ $event->id = $oldevent->id;
+ } else {
+ unset($event->id);
+ }
+ $event->name = $eventname.' ('.get_string('lessoncloses', 'lesson').')';
+ $event->timestart = $deadline;
+ $event->eventtype = 'close';
+ calendar_event::create($event);
+ }
+ }
+ }
+ }
+
+ // Delete any leftover events.
+ foreach ($oldevents as $badevent) {
+ $badevent = calendar_event::load($badevent);
+ $badevent->delete();
+ }
+}
+
+/**
+ * This standard function will check all instances of this module
+ * and make sure there are up-to-date events created for each of them.
+ * If courseid = 0, then every lesson event in the site is checked, else
+ * only lesson events belonging to the course specified are checked.
+ * This function is used, in its new format, by restore_refresh_events()
+ *
+ * @param int $courseid
+ * @return bool
+ */
+function lesson_refresh_events($courseid = 0) {
+ global $DB;
+
+ if ($courseid == 0) {
+ if (!$lessons = $DB->get_records('lessons')) {
+ return true;
+ }
+ } else {
+ if (!$lessons = $DB->get_records('lesson', array('course' => $courseid))) {
+ return true;
+ }
+ }
+
+ foreach ($lessons as $lesson) {
+ lesson_update_events($lesson);
+ }
+
+ return true;
+}
/**
* Given an ID of an instance of this module,
* @return void
**/
function lesson_process_post_save(&$lesson) {
- global $DB, $CFG;
- require_once($CFG->dirroot.'/calendar/lib.php');
- require_once($CFG->dirroot . '/mod/lesson/locallib.php');
-
- if ($events = $DB->get_records('event', array('modulename'=>'lesson', 'instance'=>$lesson->id))) {
- foreach($events as $event) {
- $event = calendar_event::load($event->id);
- $event->delete();
- }
- }
-
- $event = new stdClass;
- $event->description = $lesson->name;
- $event->courseid = $lesson->course;
- $event->groupid = 0;
- $event->userid = 0;
- $event->modulename = 'lesson';
- $event->instance = $lesson->id;
- $event->eventtype = 'open';
- $event->timestart = $lesson->available;
-
- $event->visible = instance_is_visible('lesson', $lesson);
-
- $event->timeduration = ($lesson->deadline - $lesson->available);
-
- if ($lesson->deadline and $lesson->available and $event->timeduration <= LESSON_MAX_EVENT_LENGTH) {
- // Single event for the whole lesson.
- $event->name = $lesson->name;
- calendar_event::create(clone($event));
- } else {
- // Separate start and end events.
- $event->timeduration = 0;
- if ($lesson->available) {
- $event->name = $lesson->name.' ('.get_string('lessonopens', 'lesson').')';
- calendar_event::create(clone($event));
- }
-
- if ($lesson->deadline) {
- $event->name = $lesson->name.' ('.get_string('lessoncloses', 'lesson').')';
- $event->timestart = $lesson->deadline;
- $event->eventtype = 'close';
- calendar_event::create(clone($event));
- }
- }
+ // Update the events relating to this lesson.
+ lesson_update_events($lesson);
}
function lesson_reset_course_form_definition(&$mform) {
$mform->addElement('header', 'lessonheader', get_string('modulenameplural', 'lesson'));
$mform->addElement('advcheckbox', 'reset_lesson', get_string('deleteallattempts','lesson'));
+ $mform->addElement('advcheckbox', 'reset_lesson_user_overrides',
+ get_string('removealluseroverrides', 'lesson'));
+ $mform->addElement('advcheckbox', 'reset_lesson_group_overrides',
+ get_string('removeallgroupoverrides', 'lesson'));
}
/**
* @return array
*/
function lesson_reset_course_form_defaults($course) {
- return array('reset_lesson'=>1);
+ return array('reset_lesson' => 1,
+ 'reset_lesson_group_overrides' => 1,
+ 'reset_lesson_user_overrides' => 1);
}
/**
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallattempts', 'lesson'), 'error'=>false);
}
+ // Remove user overrides.
+ if (!empty($data->reset_lesson_user_overrides)) {
+ $DB->delete_records_select('lesson_overrides',
+ 'lessonid IN (SELECT id FROM {lesson} WHERE course = ?) AND userid IS NOT NULL', array($data->courseid));
+ $status[] = array(
+ 'component' => $componentstr,
+ 'item' => get_string('useroverridesdeleted', 'lesson'),
+ 'error' => false);
+ }
+ // Remove group overrides.
+ if (!empty($data->reset_lesson_group_overrides)) {
+ $DB->delete_records_select('lesson_overrides',
+ 'lessonid IN (SELECT id FROM {lesson} WHERE course = ?) AND groupid IS NOT NULL', array($data->courseid));
+ $status[] = array(
+ 'component' => $componentstr,
+ 'item' => get_string('groupoverridesdeleted', 'lesson'),
+ 'error' => false);
+ }
/// updating dates - shift may be negative too
if ($data->timeshift) {
+ $DB->execute("UPDATE {lesson_overrides}
+ SET available = available + ?
+ WHERE lessonid IN (SELECT id FROM {lesson} WHERE course = ?)
+ AND available <> 0", array($data->timeshift, $data->courseid));
+ $DB->execute("UPDATE {lesson_overrides}
+ SET deadline = deadline + ?
+ WHERE lessonid IN (SELECT id FROM {lesson} WHERE course = ?)
+ AND deadline <> 0", array($data->timeshift, $data->courseid));
+
shift_course_mod_dates('lesson', array('available', 'deadline'), $data->timeshift, $data->courseid);
$status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
}
function lesson_extend_settings_navigation($settings, $lessonnode) {
global $PAGE, $DB;
+ // We want to add these new nodes after the Edit settings node, and before the
+ // Locally assigned roles node. Of course, both of those are controlled by capabilities.
+ $keys = $lessonnode->get_children_key_list();
+ $beforekey = null;
+ $i = array_search('modedit', $keys);
+ if ($i === false and array_key_exists(0, $keys)) {
+ $beforekey = $keys[0];
+ } else if (array_key_exists($i + 1, $keys)) {
+ $beforekey = $keys[$i + 1];
+ }
+
+ if (has_capability('mod/lesson:manageoverrides', $PAGE->cm->context)) {
+ $url = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $PAGE->cm->id));
+ $node = navigation_node::create(get_string('groupoverrides', 'lesson'),
+ new moodle_url($url, array('mode' => 'group')),
+ navigation_node::TYPE_SETTING, null, 'mod_lesson_groupoverrides');
+ $lessonnode->add_node($node, $beforekey);
+
+ $node = navigation_node::create(get_string('useroverrides', 'lesson'),
+ new moodle_url($url, array('mode' => 'user')),
+ navigation_node::TYPE_SETTING, null, 'mod_lesson_useroverrides');
+ $lessonnode->add_node($node, $beforekey);
+ }
+
if (has_capability('mod/lesson:edit', $PAGE->cm->context)) {
$url = new moodle_url('/mod/lesson/view.php', array('id' => $PAGE->cm->id));
$lessonnode->add(get_string('preview', 'lesson'), $url);
return $code;
}
+/**
+ * Logic to happen when a/some group(s) has/have been deleted in a course.
+ *
+ * @param int $courseid The course ID.
+ * @param int $groupid The group id if it is known
+ * @return void
+ */
+function lesson_process_group_deleted_in_course($courseid, $groupid = null) {
+ global $DB;
+
+ $params = array('courseid' => $courseid);
+ if ($groupid) {
+ $params['groupid'] = $groupid;
+ // We just update the group that was deleted.
+ $sql = "SELECT o.id, o.lessonid
+ FROM {lesson_overrides} o
+ JOIN {lesson} lesson ON lesson.id = o.lessonid
+ WHERE lesson.course = :courseid
+ AND o.groupid = :groupid";
+ } else {
+ // No groupid, we update all orphaned group overrides for all lessons in course.
+ $sql = "SELECT o.id, o.lessonid
+ FROM {lesson_overrides} o
+ JOIN {lesson} lesson ON lesson.id = o.lessonid
+ LEFT JOIN {groups} grp ON grp.id = o.groupid
+ WHERE lesson.course = :courseid
+ AND o.groupid IS NOT NULL
+ AND grp.id IS NULL";
+ }
+ $records = $DB->get_records_sql_menu($sql, $params);
+ if (!$records) {
+ return; // Nothing to do.
+ }
+ $DB->delete_records_list('lesson_overrides', 'id', array_keys($records));
+}
/**
* Abstract class that page type's MUST inherit from.
$cm = get_coursemodule_from_instance('lesson', $this->properties->id, $this->properties->course);
$context = context_module::instance($cm->id);
+ $this->delete_all_overrides();
+
$DB->delete_records("lesson", array("id"=>$this->properties->id));
$DB->delete_records("lesson_pages", array("lessonid"=>$this->properties->id));
$DB->delete_records("lesson_answers", array("lessonid"=>$this->properties->id));
return true;
}
+ /**
+ * Deletes a lesson override from the database and clears any corresponding calendar events
+ *
+ * @param int $overrideid The id of the override being deleted
+ * @return bool true on success
+ */
+ public function delete_override($overrideid) {
+ global $CFG, $DB;
+
+ require_once($CFG->dirroot . '/calendar/lib.php');
+
+ $cm = get_coursemodule_from_instance('lesson', $this->properties->id, $this->properties->course);
+
+ $override = $DB->get_record('lesson_overrides', array('id' => $overrideid), '*', MUST_EXIST);
+
+ // Delete the events.
+ $conds = array('modulename' => 'lesson',
+ 'instance' => $this->properties->id);
+ if (isset($override->userid)) {
+ $conds['userid'] = $override->userid;
+ } else {
+ $conds['groupid'] = $override->groupid;
+ }
+ $events = $DB->get_records('event', $conds);
+ foreach ($events as $event) {
+ $eventold = calendar_event::load($event);
+ $eventold->delete();
+ }
+
+ $DB->delete_records('lesson_overrides', array('id' => $overrideid));
+
+ // Set the common parameters for one of the events we will be triggering.
+ $params = array(
+ 'objectid' => $override->id,
+ 'context' => context_module::instance($cm->id),
+ 'other' => array(
+ 'lessonid' => $override->lessonid
+ )
+ );
+ // Determine which override deleted event to fire.
+ if (!empty($override->userid)) {
+ $params['relateduserid'] = $override->userid;
+ $event = \mod_lesson\event\user_override_deleted::create($params);
+ } else {
+ $params['other']['groupid'] = $override->groupid;
+ $event = \mod_lesson\event\group_override_deleted::create($params);
+ }
+
+ // Trigger the override deleted event.
+ $event->add_record_snapshot('lesson_overrides', $override);
+ $event->trigger();
+
+ return true;
+ }
+
+ /**
+ * Deletes all lesson overrides from the database and clears any corresponding calendar events
+ */
+ public function delete_all_overrides() {
+ global $DB;
+
+ $overrides = $DB->get_records('lesson_overrides', array('lessonid' => $this->properties->id), 'id');
+ foreach ($overrides as $override) {
+ $this->delete_override($override->id);
+ }
+ }
+
+ /**
+ * Updates the lesson properties with override information for a user.
+ *
+ * Algorithm: For each lesson setting, if there is a matching user-specific override,
+ * then use that otherwise, if there are group-specific overrides, return the most
+ * lenient combination of them. If neither applies, leave the quiz setting unchanged.
+ *
+ * Special case: if there is more than one password that applies to the user, then
+ * lesson->extrapasswords will contain an array of strings giving the remaining
+ * passwords.
+ *
+ * @param int $userid The userid.
+ */
+ public function update_effective_access($userid) {
+ global $DB;
+
+ // Check for user override.
+ $override = $DB->get_record('lesson_overrides', array('lessonid' => $this->properties->id, 'userid' => $userid));
+
+ if (!$override) {
+ $override = new stdClass();
+ $override->available = null;
+ $override->deadline = null;
+ $override->timelimit = null;
+ $override->review = null;
+ $override->maxattempts = null;
+ $override->retake = null;
+ $override->password = null;
+ }
+
+ // Check for group overrides.
+ $groupings = groups_get_user_groups($this->properties->course, $userid);
+
+ if (!empty($groupings[0])) {
+ // Select all overrides that apply to the User's groups.
+ list($extra, $params) = $DB->get_in_or_equal(array_values($groupings[0]));
+ $sql = "SELECT * FROM {lesson_overrides}
+ WHERE groupid $extra AND lessonid = ?";
+ $params[] = $this->properties->id;
+ $records = $DB->get_records_sql($sql, $params);
+
+ // Combine the overrides.
+ $availables = array();
+ $deadlines = array();
+ $timelimits = array();
+ $reviews = array();
+ $attempts = array();
+ $retakes = array();
+ $passwords = array();
+
+ foreach ($records as $gpoverride) {
+ if (isset($gpoverride->available)) {
+ $availables[] = $gpoverride->available;
+ }
+ if (isset($gpoverride->deadline)) {
+ $deadlines[] = $gpoverride->deadline;
+ }
+ if (isset($gpoverride->timelimit)) {
+ $timelimits[] = $gpoverride->timelimit;
+ }
+ if (isset($gpoverride->review)) {
+ $reviews[] = $gpoverride->review;
+ }
+ if (isset($gpoverride->maxattempts)) {
+ $attempts[] = $gpoverride->maxattempts;
+ }
+ if (isset($gpoverride->retake)) {
+ $retakes[] = $gpoverride->retake;
+ }
+ if (isset($gpoverride->password)) {
+ $passwords[] = $gpoverride->password;
+ }
+ }
+ // If there is a user override for a setting, ignore the group override.
+ if (is_null($override->available) && count($availables)) {
+ $override->available = min($availables);
+ }
+ if (is_null($override->deadline) && count($deadlines)) {
+ if (in_array(0, $deadlines)) {
+ $override->deadline = 0;
+ } else {
+ $override->deadline = max($deadlines);
+ }
+ }
+ if (is_null($override->timelimit) && count($timelimits)) {
+ if (in_array(0, $timelimits)) {
+ $override->timelimit = 0;
+ } else {
+ $override->timelimit = max($timelimits);
+ }
+ }
+ if (is_null($override->review) && count($reviews)) {
+ $override->review = max($reviews);
+ }
+ if (is_null($override->maxattempts) && count($attempts)) {
+ $override->maxattempts = max($attempts);
+ }
+ if (is_null($override->retake) && count($retakes)) {
+ $override->retake = max($retakes);
+ }
+ if (is_null($override->password) && count($passwords)) {
+ $override->password = array_shift($passwords);
+ if (count($passwords)) {
+ $override->extrapasswords = $passwords;
+ }
+ }
+
+ }
+
+ // Merge with lesson defaults.
+ $keys = array('available', 'deadline', 'timelimit', 'maxattempts', 'review', 'retake');
+ foreach ($keys as $key) {
+ if (isset($override->{$key})) {
+ $this->properties->{$key} = $override->{$key};
+ }
+ }
+
+ // Special handling of lesson usepassword and password.
+ if (isset($override->password)) {
+ if ($override->password == '') {
+ $this->properties->usepassword = 0;
+ } else {
+ $this->properties->usepassword = 1;
+ $this->properties->password = $override->password;
+ if (isset($override->extrapasswords)) {
+ $this->properties->extrapasswords = $override->extrapasswords;
+ }
+ }
+ }
+ }
+
/**
* Fetches messages from the session that may have been set in previous page
* actions.
* @return string
*/
public function time_remaining($starttime) {
- $timeleft = $starttime + $this->timelimit - time();
+ $timeleft = $starttime + $this->properties->timelimit - time();
$hours = floor($timeleft/3600);
$timeleft = $timeleft - ($hours * 3600);
$minutes = floor($timeleft/60);
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);
if (!empty($userpassword) && (($lesson->password == md5(trim($userpassword))) || ($lesson->password == trim($userpassword)))) {
// with or without md5 for backward compatibility (MDL-11090)
$USER->lessonloggedin[$lesson->id] = true;
+ $correctpass = true;
if ($lesson->highscores) {
// Logged in - redirect so we go through all of these checks before starting the lesson.
redirect("$CFG->wwwroot/mod/lesson/view.php?id=$cm->id");
}
- } else {
+ } 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) {
+ $correctpass = true;
+ $USER->lessonloggedin[$lesson->id] = true;
+ if ($lesson->highscores) {
+ // Logged in - redirect so we go through all of these checks before starting the lesson.
+ redirect("$CFG->wwwroot/mod/lesson/view.php?id=$cm->id");
+ }
+ }
+ }
+ }
+ if (!$correctpass) {
echo $lessonoutput->header($lesson, $cm);
echo $lessonoutput->login_prompt($lesson, $userpassword !== '');
echo $lessonoutput->footer();
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Settings form for overrides in the lesson module.
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir . '/formslib.php');
+require_once($CFG->dirroot . '/mod/lesson/mod_form.php');
+
+
+/**
+ * Form for editing settings overrides.
+ *
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class lesson_override_form extends moodleform {
+
+ /** @var object course module object. */
+ protected $cm;
+
+ /** @var object the lesson settings object. */
+ protected $lesson;
+
+ /** @var context the lesson context. */
+ protected $context;
+
+ /** @var bool editing group override (true) or user override (false). */
+ protected $groupmode;
+
+ /** @var int groupid, if provided. */
+ protected $groupid;
+
+ /** @var int userid, if provided. */
+ protected $userid;
+
+ /**
+ * Constructor.
+ * @param moodle_url $submiturl the form action URL.
+ * @param object $cm course module object.
+ * @param object $lesson the lesson settings object.
+ * @param object $context the lesson context.
+ * @param bool $groupmode editing group override (true) or user override (false).
+ * @param object $override the override being edited, if it already exists.
+ */
+ public function __construct($submiturl, $cm, $lesson, $context, $groupmode, $override) {
+
+ $this->cm = $cm;
+ $this->lesson = $lesson;
+ $this->context = $context;
+ $this->groupmode = $groupmode;
+ $this->groupid = empty($override->groupid) ? 0 : $override->groupid;
+ $this->userid = empty($override->userid) ? 0 : $override->userid;
+
+ parent::__construct($submiturl, null, 'post');
+
+ }
+
+ /**
+ * Define this form - called by the parent constructor
+ */
+ protected function definition() {
+ global $CFG, $DB;
+
+ $cm = $this->cm;
+ $mform = $this->_form;
+
+ $mform->addElement('header', 'override', get_string('override', 'lesson'));
+
+ if ($this->groupmode) {
+ // Group override.
+ if ($this->groupid) {
+ // There is already a groupid, so freeze the selector.
+ $groupchoices = array();
+ $groupchoices[$this->groupid] = groups_get_group_name($this->groupid);
+ $mform->addElement('select', 'groupid',
+ get_string('overridegroup', 'lesson'), $groupchoices);
+ $mform->freeze('groupid');
+ } else {
+ // Prepare the list of groups.
+ $groups = groups_get_all_groups($cm->course);
+ if (empty($groups)) {
+ // Generate an error.
+ $link = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id));
+ print_error('groupsnone', 'lesson', $link);
+ }
+
+ $groupchoices = array();
+ foreach ($groups as $group) {
+ $groupchoices[$group->id] = $group->name;
+ }
+ unset($groups);
+
+ if (count($groupchoices) == 0) {
+ $groupchoices[0] = get_string('none');
+ }
+
+ $mform->addElement('select', 'groupid',
+ get_string('overridegroup', 'lesson'), $groupchoices);
+ $mform->addRule('groupid', get_string('required'), 'required', null, 'client');
+ }
+ } else {
+ // User override.
+ if ($this->userid) {
+ // There is already a userid, so freeze the selector.
+ $user = $DB->get_record('user', array('id' => $this->userid));
+ $userchoices = array();
+ $userchoices[$this->userid] = fullname($user);
+ $mform->addElement('select', 'userid',
+ get_string('overrideuser', 'lesson'), $userchoices);
+ $mform->freeze('userid');
+ } else {
+ // Prepare the list of users.
+ $users = get_enrolled_users($this->context, '', 0,
+ 'u.id, u.email, ' . get_all_user_name_fields(true, 'u'));
+
+ // Filter users based on any fixed restrictions (groups, profile).
+ $info = new \core_availability\info_module($cm);
+ $users = $info->filter_user_list($users);
+
+ if (empty($users)) {
+ // Generate an error.
+ $link = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id));
+ print_error('usersnone', 'lesson', $link);
+ }
+
+ $userchoices = array();
+ $canviewemail = in_array('email', get_extra_user_fields($this->context));
+ foreach ($users as $id => $user) {
+ if (empty($invalidusers[$id]) || (!empty($override) &&
+ $id == $override->userid)) {
+ if ($canviewemail) {
+ $userchoices[$id] = fullname($user) . ', ' . $user->email;
+ } else {
+ $userchoices[$id] = fullname($user);
+ }
+ }
+ }
+ unset($users);
+
+ if (count($userchoices) == 0) {
+ $userchoices[0] = get_string('none');
+ }
+ $mform->addElement('searchableselector', 'userid',
+ get_string('overrideuser', 'lesson'), $userchoices);
+ $mform->addRule('userid', get_string('required'), 'required', null, 'client');
+ }
+ }
+
+ // Password.
+ // This field has to be above the date and timelimit fields,
+ // otherwise browsers will clear it when those fields are changed.
+ $mform->addElement('passwordunmask', 'password', get_string('usepassword', 'lesson'));
+ $mform->setType('password', PARAM_TEXT);
+ $mform->addHelpButton('password', 'usepassword', 'lesson');
+ $mform->setDefault('password', $this->lesson->password);;
+
+ // Open and close dates.
+ $mform->addElement('date_time_selector', 'available', get_string('available', 'lesson'), array('optional' => true));
+ $mform->setDefault('available', $this->lesson->available);
+
+ $mform->addElement('date_time_selector', 'deadline', get_string('deadline', 'lesson'), array('optional' => true));
+ $mform->setDefault('deadline', $this->lesson->deadline);
+
+ // Lesson time limit.
+ $mform->addElement('duration', 'timelimit',
+ get_string('timelimit', 'lesson'), array('optional' => true));
+ if ($this->lesson->timelimit != 0) {
+ $mform->setDefault('timelimit', 0);
+ } else {
+ $mform->setDefault('timelimit', $this->lesson->timelimit);
+ }
+
+ // Try a question again.
+ $mform->addElement('selectyesno', 'review', get_string('displayreview', 'lesson'));
+ $mform->addHelpButton('review', 'displayreview', 'lesson');
+ $mform->setDefault('review', $this->lesson->review);
+
+ // Number of attempts.
+ $numbers = array();
+ for ($i = 10; $i > 0; $i--) {
+ $numbers[$i] = $i;
+ }
+ $mform->addElement('select', 'maxattempts', get_string('maximumnumberofattempts', 'lesson'), $numbers);
+ $mform->addHelpButton('maxattempts', 'maximumnumberofattempts', 'lesson');
+ $mform->setDefault('maxattempts', $this->lesson->maxattempts);
+
+ // Retake allowed.
+ $mform->addElement('selectyesno', 'retake', get_string('retakesallowed', 'lesson'));
+ $mform->addHelpButton('retake', 'retakesallowed', 'lesson');
+ $mform->setDefault('retake', $this->lesson->retake);
+
+ // Submit buttons.
+ $mform->addElement('submit', 'resetbutton',
+ get_string('reverttodefaults', 'lesson'));
+
+ $buttonarray = array();
+ $buttonarray[] = $mform->createElement('submit', 'submitbutton',
+ get_string('save', 'lesson'));
+ $buttonarray[] = $mform->createElement('submit', 'againbutton',
+ get_string('saveoverrideandstay', 'lesson'));
+ $buttonarray[] = $mform->createElement('cancel');
+
+ $mform->addGroup($buttonarray, 'buttonbar', '', array(' '), false);
+ $mform->closeHeaderBefore('buttonbar');
+
+ }
+
+ /**
+ * Validate the submitted form data.
+ *
+ * @param array $data array of ("fieldname"=>value) of submitted data
+ * @param array $files array of uploaded files "element_name"=>tmp_file_path
+ * @return array of "element_name"=>"error_description" if there are errors
+ */
+ public function validation($data, $files) {
+ global $COURSE, $DB;
+ $errors = parent::validation($data, $files);
+
+ $mform =& $this->_form;
+ $lesson = $this->lesson;
+
+ if ($mform->elementExists('userid')) {
+ if (empty($data['userid'])) {
+ $errors['userid'] = get_string('required');
+ }
+ }
+
+ if ($mform->elementExists('groupid')) {
+ if (empty($data['groupid'])) {
+ $errors['groupid'] = get_string('required');
+ }
+ }
+
+ // Ensure that the dates make sense.
+ if (!empty($data['available']) && !empty($data['deadline'])) {
+ if ($data['deadline'] < $data['available'] ) {
+ $errors['deadline'] = get_string('closebeforeopen', 'lesson');
+ }
+ }
+
+ // Ensure that at least one lesson setting was changed.
+ $changed = false;
+ $keys = array('available', 'deadline', 'review', 'timelimit', 'maxattempts', 'retake', 'password');
+ foreach ($keys as $key) {
+ if ($data[$key] != $lesson->{$key}) {
+ $changed = true;
+ break;
+ }
+ }
+
+ if (!$changed) {
+ $errors['available'] = get_string('nooverridedata', 'lesson');
+ }
+
+ return $errors;
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This page handles deleting lesson overrides
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+require_once(dirname(__FILE__) . '/../../config.php');
+require_once($CFG->dirroot.'/mod/lesson/lib.php');
+require_once($CFG->dirroot.'/mod/lesson/locallib.php');
+require_once($CFG->dirroot.'/mod/lesson/override_form.php');
+
+$overrideid = required_param('id', PARAM_INT);
+$confirm = optional_param('confirm', false, PARAM_BOOL);
+
+if (! $override = $DB->get_record('lesson_overrides', array('id' => $overrideid))) {
+ print_error('invalidoverrideid', 'lesson');
+}
+
+$lesson = new lesson($DB->get_record('lesson', array('id' => $override->lessonid), '*', MUST_EXIST));
+
+if (! $cm = get_coursemodule_from_instance("lesson", $lesson->id, $lesson->course)) {
+ print_error('invalidcoursemodule');
+}
+$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+$context = context_module::instance($cm->id);
+
+require_login($course, false, $cm);
+
+// Check the user has the required capabilities to modify an override.
+require_capability('mod/lesson:manageoverrides', $context);
+
+$url = new moodle_url('/mod/lesson/overridedelete.php', array('id' => $override->id));
+$confirmurl = new moodle_url($url, array('id' => $override->id, 'confirm' => 1));
+$cancelurl = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id));
+
+if (!empty($override->userid)) {
+ $cancelurl->param('mode', 'user');
+}
+
+// If confirm is set (PARAM_BOOL) then we have confirmation of intention to delete.
+if ($confirm) {
+ require_sesskey();
+
+ $lesson->delete_override($override->id);
+
+ redirect($cancelurl);
+}
+
+// Prepare the page to show the confirmation form.
+$stroverride = get_string('override', 'lesson');
+$title = get_string('deletecheck', null, $stroverride);
+
+$PAGE->set_url($url);
+$PAGE->set_pagelayout('admin');
+$PAGE->navbar->add($title);
+$PAGE->set_title($title);
+$PAGE->set_heading($course->fullname);
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading(format_string($lesson->name, true, array('context' => $context)));
+
+if ($override->groupid) {
+ $group = $DB->get_record('groups', array('id' => $override->groupid), 'id, name');
+ $confirmstr = get_string("overridedeletegroupsure", "lesson", $group->name);
+} else {
+ $namefields = get_all_user_name_fields(true);
+ $user = $DB->get_record('user', array('id' => $override->userid),
+ 'id, ' . $namefields);
+ $confirmstr = get_string("overridedeleteusersure", "lesson", fullname($user));
+}
+
+echo $OUTPUT->confirm($confirmstr, $confirmurl, $cancelurl);
+
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This page handles editing and creation of lesson overrides
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+require_once(dirname(__FILE__) . '/../../config.php');
+require_once($CFG->dirroot.'/mod/lesson/lib.php');
+require_once($CFG->dirroot.'/mod/lesson/locallib.php');
+require_once($CFG->dirroot.'/mod/lesson/override_form.php');
+
+
+$cmid = optional_param('cmid', 0, PARAM_INT);
+$overrideid = optional_param('id', 0, PARAM_INT);
+$action = optional_param('action', null, PARAM_ALPHA);
+$reset = optional_param('reset', false, PARAM_BOOL);
+
+$override = null;
+if ($overrideid) {
+
+ if (! $override = $DB->get_record('lesson_overrides', array('id' => $overrideid))) {
+ print_error('invalidoverrideid', 'lesson');
+ }
+
+ $lesson = new lesson($DB->get_record('lesson', array('id' => $override->lessonid), '*', MUST_EXIST));
+
+ list($course, $cm) = get_course_and_cm_from_instance($lesson, 'lesson');
+
+} else if ($cmid) {
+ list($course, $cm) = get_course_and_cm_from_cmid($cmid, 'lesson');
+ $lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
+
+} else {
+ print_error('invalidcoursemodule');
+}
+$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+$url = new moodle_url('/mod/lesson/overrideedit.php');
+if ($action) {
+ $url->param('action', $action);
+}
+if ($overrideid) {
+ $url->param('id', $overrideid);
+} else {
+ $url->param('cmid', $cmid);
+}
+
+$PAGE->set_url($url);
+
+require_login($course, false, $cm);
+
+$context = context_module::instance($cm->id);
+
+// Add or edit an override.
+require_capability('mod/lesson:manageoverrides', $context);
+
+if ($overrideid) {
+ // Editing an override.
+ $data = clone $override;
+} else {
+ // Creating a new override.
+ $data = new stdClass();
+}
+
+// Merge lesson defaults with data.
+$keys = array('available', 'deadline', 'review', 'timelimit', 'maxattempts', 'retake', 'password');
+foreach ($keys as $key) {
+ if (!isset($data->{$key}) || $reset) {
+ $data->{$key} = $lesson->{$key};
+ }
+}
+
+// True if group-based override.
+$groupmode = !empty($data->groupid) || ($action === 'addgroup' && empty($overrideid));
+
+// If we are duplicating an override, then clear the user/group and override id
+// since they will change.
+if ($action === 'duplicate') {
+ $override->id = $data->id = null;
+ $override->userid = $data->userid = null;
+ $override->groupid = $data->groupid = null;
+}
+
+$overridelisturl = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id));
+if (!$groupmode) {
+ $overridelisturl->param('mode', 'user');
+}
+
+// Setup the form.
+$mform = new lesson_override_form($url, $cm, $lesson, $context, $groupmode, $override);
+$mform->set_data($data);
+
+if ($mform->is_cancelled()) {
+ redirect($overridelisturl);
+
+} else if (optional_param('resetbutton', 0, PARAM_ALPHA)) {
+ $url->param('reset', true);
+ redirect($url);
+
+} else if ($fromform = $mform->get_data()) {
+ // Process the data.
+ $fromform->lessonid = $lesson->id;
+
+ // Replace unchanged values with null.
+ foreach ($keys as $key) {
+ if ($fromform->{$key} == $lesson->{$key}) {
+ $fromform->{$key} = null;
+ }
+ }
+
+ // See if we are replacing an existing override.
+ $userorgroupchanged = false;
+ if (empty($override->id)) {
+ $userorgroupchanged = true;
+ } else if (!empty($fromform->userid)) {
+ $userorgroupchanged = $fromform->userid !== $override->userid;
+ } else {
+ $userorgroupchanged = $fromform->groupid !== $override->groupid;
+ }
+
+ if ($userorgroupchanged) {
+ $conditions = array(
+ 'lessonid' => $lesson->id,
+ 'userid' => empty($fromform->userid) ? null : $fromform->userid,
+ 'groupid' => empty($fromform->groupid) ? null : $fromform->groupid);
+ if ($oldoverride = $DB->get_record('lesson_overrides', $conditions)) {
+ // There is an old override, so we merge any new settings on top of
+ // the older override.
+ foreach ($keys as $key) {
+ if (is_null($fromform->{$key})) {
+ $fromform->{$key} = $oldoverride->{$key};
+ }
+ }
+
+ $lesson->delete_override($oldoverride->id);
+ }
+ }
+
+ // Set the common parameters for one of the events we may be triggering.
+ $params = array(
+ 'context' => $context,
+ 'other' => array(
+ 'lessonid' => $lesson->id
+ )
+ );
+ if (!empty($override->id)) {
+ $fromform->id = $override->id;
+ $DB->update_record('lesson_overrides', $fromform);
+
+ // Determine which override updated event to fire.
+ $params['objectid'] = $override->id;
+ if (!$groupmode) {
+ $params['relateduserid'] = $fromform->userid;
+ $event = \mod_lesson\event\user_override_updated::create($params);
+ } else {
+ $params['other']['groupid'] = $fromform->groupid;
+ $event = \mod_lesson\event\group_override_updated::create($params);
+ }
+
+ // Trigger the override updated event.
+ $event->trigger();
+ } else {
+ unset($fromform->id);
+ $fromform->id = $DB->insert_record('lesson_overrides', $fromform);
+
+ // Determine which override created event to fire.
+ $params['objectid'] = $fromform->id;
+ if (!$groupmode) {
+ $params['relateduserid'] = $fromform->userid;
+ $event = \mod_lesson\event\user_override_created::create($params);
+ } else {
+ $params['other']['groupid'] = $fromform->groupid;
+ $event = \mod_lesson\event\group_override_created::create($params);
+ }
+
+ // Trigger the override created event.
+ $event->trigger();
+ }
+
+ lesson_update_events($lesson, $fromform);
+
+ if (!empty($fromform->submitbutton)) {
+ redirect($overridelisturl);
+ }
+
+ // The user pressed the 'again' button, so redirect back to this page.
+ $url->remove_params('cmid');
+ $url->param('action', 'duplicate');
+ $url->param('id', $fromform->id);
+ redirect($url);
+
+}
+
+// Print the form.
+$pagetitle = get_string('editoverride', 'lesson');
+$PAGE->navbar->add($pagetitle);
+$PAGE->set_pagelayout('admin');
+$PAGE->set_title($pagetitle);
+$PAGE->set_heading($course->fullname);
+echo $OUTPUT->header();
+echo $OUTPUT->heading(format_string($lesson->name, true, array('context' => $context)));
+
+$mform->display();
+
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This page handles listing of lesson overrides
+ *
+ * @package mod_lesson
+ * @copyright 2015 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+require_once(dirname(__FILE__) . '/../../config.php');
+require_once($CFG->dirroot.'/mod/lesson/lib.php');
+require_once($CFG->dirroot.'/mod/lesson/locallib.php');
+require_once($CFG->dirroot.'/mod/lesson/override_form.php');
+
+
+$cmid = required_param('cmid', PARAM_INT);
+$mode = optional_param('mode', '', PARAM_ALPHA); // One of 'user' or 'group', default is 'group'.
+
+list($course, $cm) = get_course_and_cm_from_cmid($cmid, 'lesson');
+$lesson = $DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST);
+
+// Get the course groups.
+$groups = groups_get_all_groups($cm->course);
+if ($groups === false) {
+ $groups = array();
+}
+
+// Default mode is "group", unless there are no groups.
+if ($mode != "user" and $mode != "group") {
+ if (!empty($groups)) {
+ $mode = "group";
+ } else {
+ $mode = "user";
+ }
+}
+$groupmode = ($mode == "group");
+
+$url = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id, 'mode' => $mode));
+
+$PAGE->set_url($url);
+
+require_login($course, false, $cm);
+
+$context = context_module::instance($cm->id);
+
+// Check the user has the required capabilities to list overrides.
+require_capability('mod/lesson:manageoverrides', $context);
+
+// Display a list of overrides.
+$PAGE->set_pagelayout('admin');
+$PAGE->set_title(get_string('overrides', 'lesson'));
+$PAGE->set_heading($course->fullname);
+echo $OUTPUT->header();
+echo $OUTPUT->heading(format_string($lesson->name, true, array('context' => $context)));
+
+// Delete orphaned group overrides.
+$sql = 'SELECT o.id
+ FROM {lesson_overrides} o LEFT JOIN {groups} g
+ ON o.groupid = g.id
+ WHERE o.groupid IS NOT NULL
+ AND g.id IS NULL
+ AND o.lessonid = ?';
+$params = array($lesson->id);
+$orphaned = $DB->get_records_sql($sql, $params);
+if (!empty($orphaned)) {
+ $DB->delete_records_list('lesson_overrides', 'id', array_keys($orphaned));
+}
+
+// Fetch all overrides.
+if ($groupmode) {
+ $colname = get_string('group');
+ $sql = 'SELECT o.*, g.name
+ FROM {lesson_overrides} o
+ JOIN {groups} g ON o.groupid = g.id
+ WHERE o.lessonid = :lessonid
+ ORDER BY g.name';
+ $params = array('lessonid' => $lesson->id);
+} else {
+ $colname = get_string('user');
+ list($sort, $params) = users_order_by_sql('u');
+ $sql = 'SELECT o.*, ' . get_all_user_name_fields(true, 'u') . '
+ FROM {lesson_overrides} o
+ JOIN {user} u ON o.userid = u.id
+ WHERE o.lessonid = :lessonid
+ ORDER BY ' . $sort;
+ $params['lessonid'] = $lesson->id;
+}
+
+$overrides = $DB->get_records_sql($sql, $params);
+
+// Initialise table.
+$table = new html_table();
+$table->headspan = array(1, 2, 1);
+$table->colclasses = array('colname', 'colsetting', 'colvalue', 'colaction');
+$table->head = array(
+ $colname,
+ get_string('overrides', 'lesson'),
+ get_string('action'),
+);
+
+$userurl = new moodle_url('/user/view.php', array());
+$groupurl = new moodle_url('/group/overview.php', array('id' => $cm->course));
+
+$overridedeleteurl = new moodle_url('/mod/lesson/overridedelete.php');
+$overrideediturl = new moodle_url('/mod/lesson/overrideedit.php');
+
+$hasinactive = false; // Whether there are any inactive overrides.
+
+foreach ($overrides as $override) {
+
+ $fields = array();
+ $values = array();
+ $active = true;
+
+ // Check for inactive overrides.
+ if (!$groupmode) {
+ if (!is_enrolled($context, $override->userid)) {
+ // User not enrolled.
+ $active = false;
+ } else if (!\core_availability\info_module::is_user_visible($cm, $override->userid)) {
+ // User cannot access the module.
+ $active = false;
+ }
+ }
+
+ // Format available.
+ if (isset($override->available)) {
+ $fields[] = get_string('lessonopens', 'lesson');
+ $values[] = $override->available > 0 ?
+ userdate($override->available) : get_string('noopen', 'lesson');
+ }
+
+ // Format deadline.
+ if (isset($override->deadline)) {
+ $fields[] = get_string('lessoncloses', 'lesson');
+ $values[] = $override->deadline > 0 ?
+ userdate($override->deadline) : get_string('noclose', 'lesson');
+ }
+
+ // Format timelimit.
+ if (isset($override->timelimit)) {
+ $fields[] = get_string('timelimit', 'lesson');
+ $values[] = $override->timelimit > 0 ?
+ format_time($override->timelimit) : get_string('none', 'lesson');
+ }
+
+ // Format option to try a question again.
+ if (isset($override->review)) {
+ $fields[] = get_string('displayreview', 'lesson');
+ $values[] = $override->review ?
+ get_string('yes') : get_string('no');
+ }
+
+ // Format number of attempts.
+ if (isset($override->maxattempts)) {
+ $fields[] = get_string('maximumnumberofattempts', 'lesson');
+ $values[] = $override->maxattempts > 0 ?
+ $override->maxattempts : get_string('unlimited');
+ }
+
+ // Format retake allowed.
+ if (isset($override->retake)) {
+ $fields[] = get_string('retakesallowed', 'lesson');
+ $values[] = $override->retake ?
+ get_string('yes') : get_string('no');
+ }
+
+ // Format password.
+ if (isset($override->password)) {
+ $fields[] = get_string('usepassword', 'lesson');
+ $values[] = $override->password !== '' ?
+ get_string('enabled', 'lesson') : get_string('none', 'lesson');
+ }
+
+ // Icons.
+ $iconstr = '';
+
+ if ($active) {
+ // Edit.
+ $editurlstr = $overrideediturl->out(true, array('id' => $override->id));
+ $iconstr = '<a title="' . get_string('edit') . '" href="'. $editurlstr . '">' .
+ '<img src="' . $OUTPUT->pix_url('t/edit') . '" class="iconsmall" alt="' .
+ get_string('edit') . '" /></a> ';
+ // Duplicate.
+ $copyurlstr = $overrideediturl->out(true,
+ array('id' => $override->id, 'action' => 'duplicate'));
+ $iconstr .= '<a title="' . get_string('copy') . '" href="' . $copyurlstr . '">' .
+ '<img src="' . $OUTPUT->pix_url('t/copy') . '" class="iconsmall" alt="' .
+ get_string('copy') . '" /></a> ';
+ }
+ // Delete.
+ $deleteurlstr = $overridedeleteurl->out(true,
+ array('id' => $override->id, 'sesskey' => sesskey()));
+ $iconstr .= '<a title="' . get_string('delete') . '" href="' . $deleteurlstr . '">' .
+ '<img src="' . $OUTPUT->pix_url('t/delete') . '" class="iconsmall" alt="' .
+ get_string('delete') . '" /></a> ';
+
+ if ($groupmode) {
+ $usergroupstr = '<a href="' . $groupurl->out(true,
+ array('group' => $override->groupid)) . '" >' . $override->name . '</a>';
+ } else {
+ $usergroupstr = '<a href="' . $userurl->out(true,
+ array('id' => $override->userid)) . '" >' . fullname($override) . '</a>';
+ }
+
+ $class = '';
+ if (!$active) {
+ $class = "dimmed_text";
+ $usergroupstr .= '*';
+ $hasinactive = true;
+ }
+
+ $usergroupcell = new html_table_cell();
+ $usergroupcell->rowspan = count($fields);
+ $usergroupcell->text = $usergroupstr;
+ $actioncell = new html_table_cell();
+ $actioncell->rowspan = count($fields);
+ $actioncell->text = $iconstr;
+
+ for ($i = 0; $i < count($fields); ++$i) {
+ $row = new html_table_row();
+ $row->attributes['class'] = $class;
+ if ($i == 0) {
+ $row->cells[] = $usergroupcell;
+ }
+ $cell1 = new html_table_cell();
+ $cell1->text = $fields[$i];
+ $row->cells[] = $cell1;
+ $cell2 = new html_table_cell();
+ $cell2->text = $values[$i];
+ $row->cells[] = $cell2;
+ if ($i == 0) {
+ $row->cells[] = $actioncell;
+ }
+ $table->data[] = $row;
+ }
+}
+
+// Output the table and button.
+echo html_writer::start_tag('div', array('id' => 'lessonoverrides'));
+if (count($table->data)) {
+ echo html_writer::table($table);
+}
+if ($hasinactive) {
+ echo $OUTPUT->notification(get_string('inactiveoverridehelp', 'lesson'), 'dimmed_text');
+}
+
+echo html_writer::start_tag('div', array('class' => 'buttons'));
+$options = array();
+if ($groupmode) {
+ if (empty($groups)) {
+ // There are no groups.
+ echo $OUTPUT->notification(get_string('groupsnone', 'lesson'), 'error');
+ $options['disabled'] = true;
+ }
+ echo $OUTPUT->single_button($overrideediturl->out(true,
+ array('action' => 'addgroup', 'cmid' => $cm->id)),
+ get_string('addnewgroupoverride', 'lesson'), 'post', $options);
+} else {
+ $users = array();
+ // See if there are any users in the lesson.
+ $users = get_enrolled_users($context);
+ $info = new \core_availability\info_module($cm);
+ $users = $info->filter_user_list($users);
+
+ if (empty($users)) {
+ // There are no users.
+ echo $OUTPUT->notification(get_string('usersnone', 'lesson'), 'error');
+ $options['disabled'] = true;
+ }
+ echo $OUTPUT->single_button($overrideediturl->out(true,
+ array('action' => 'adduser', 'cmid' => $cm->id)),
+ get_string('addnewuseroverride', 'lesson'), 'get', $options);
+}
+echo html_writer::end_tag('div');
+echo html_writer::end_tag('div');
+
+// Finish the page.
+echo $OUTPUT->footer();
$userid = optional_param('userid', null, PARAM_INT); // if empty, then will display the general detailed view
$try = optional_param('try', null, PARAM_INT);
+ if (!empty($userid)) {
+ // Apply overrides.
+ $lesson->update_effective_access($userid);
+ }
+
$lessonpages = $lesson->load_all_pages();
foreach ($lessonpages as $lessonpage) {
if ($lessonpage->prevpageid == 0) {
--- /dev/null
+@mod @mod_lesson
+Feature: Lesson reset
+ In order to reuse past lessons
+ As a teacher
+ I need to remove all previous data.
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | teacher1 | Tina | Teacher1 | teacher1@asd.com |
+ | student1 | Sam1 | Student1 | student1@asd.com |
+ | student2 | Sam2 | Student2 | student2@asd.com |
+ And the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ And the following "groups" exist:
+ | name | course | idnumber |
+ | Group 1 | C1 | G1 |
+ | Group 2 | C1 | G2 |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber |
+ | lesson | Test lesson name | Test lesson description | C1 | lesson1 |
+ And I log in as "teacher1"
+ And I follow "Course 1"
+ And I turn editing mode on
+ And I follow "Test lesson name"
+ And I follow "Add a question page"
+ And I set the field "Select a question type" to "True/false"
+ And I press "Add a question page"
+ And I set the following fields to these values:
+ | Page title | True/false question 1 |
+ | Page contents | Cat is an amphibian |
+ | id_answer_editor_0 | False |
+ | id_response_editor_0 | Correct |
+ | id_jumpto_0 | Next page |
+ | id_answer_editor_1 | True |
+ | id_response_editor_1 | Wrong |
+ | id_jumpto_1 | This page |
+ And I press "Save page"
+
+ Scenario: Use course reset to clear all attempt data
+ When I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | False | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+ And I log out
+ And I log in as "teacher1"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I navigate to "Overview" node in "Reports"
+ And I should see "Sam1 Student1"
+ And I navigate to "Reset" node in "Course administration"
+ And I set the following fields to these values:
+ | Delete all lesson attempts | 1 |
+ And I press "Reset course"
+ And I press "Continue"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I navigate to "Overview" node in "Reports"
+ Then I should see "No attempts have been made on this lesson"
+
+ Scenario: Use course reset to remove user overrides.
+ When I follow "Test lesson name"
+ And I navigate to "User overrides" node in "Lesson administration"
+ And I press "Add user override"
+ And I set the following fields to these values:
+ | Override user | Student1 |
+ | Re-takes allowed | 1 |
+ And I press "Save"
+ And I should see "Sam1 Student1"
+ And I navigate to "Reset" node in "Course administration"
+ And I set the following fields to these values:
+ | Delete all user overrides | 1 |
+ And I press "Reset course"
+ And I press "Continue"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I navigate to "User overrides" node in "Lesson administration"
+ Then I should not see "Sam1 Student1"
+
+ Scenario: Use course reset to remove group overrides.
+ When I follow "Test lesson name"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ And I press "Add group override"
+ And I set the following fields to these values:
+ | Override group | Group 1 |
+ | Re-takes allowed | 1 |
+ And I press "Save"
+ And I should see "Group 1"
+ And I navigate to "Reset" node in "Course administration"
+ And I set the following fields to these values:
+ | Delete all group overrides | 1 |
+ And I press "Reset course"
+ And I press "Continue"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ Then I should not see "Group 1"
--- /dev/null
+@mod @mod_lesson
+Feature: Lesson user override
+ In order to grant a student special access to a lesson
+ As a teacher
+ I need to create an override for that user.
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | teacher1 | Tina | Teacher1 | teacher1@asd.com |
+ | student1 | Sam1 | Student1 | student1@asd.com |
+ | student2 | Sam2 | Student2 | student2@asd.com |
+ | student3 | Sam3 | Student3 | student3@asd.com |
+ And the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ | student3 | C1 | student |
+ And the following "groups" exist:
+ | name | course | idnumber |
+ | Group 1 | C1 | G1 |
+ | Group 2 | C1 | G2 |
+ Given the following "group members" exist:
+ | user | group |
+ | student1 | G1 |
+ | student2 | G2 |
+ | student3 | G1 |
+ And the following "activities" exist:
+ | activity | name | intro | groupmode | course | idnumber |
+ | lesson | Test lesson name | Test lesson description | 1 | C1 | lesson1 |
+ And I log in as "teacher1"
+ And I follow "Course 1"
+ And I turn editing mode on
+ And I follow "Test lesson name"
+ And I follow "Add a question page"
+ And I set the field "Select a question type" to "True/false"
+ And I press "Add a question page"
+ And I set the following fields to these values:
+ | Page title | True/false question 1 |
+ | Page contents | Cat is an amphibian |
+ | id_answer_editor_0 | False |
+ | id_response_editor_0 | Correct |
+ | id_jumpto_0 | Next page |
+ | id_answer_editor_1 | True |
+ | id_response_editor_1 | Wrong |
+ | id_jumpto_1 | This page |
+ And I press "Save page"
+
+ Scenario: Add, modify then delete a group override
+ When I follow "Test lesson name"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ And I press "Add group override"
+ And I set the following fields to these values:
+ | Override group | Group 1 |
+ | id_deadline_enabled | 1 |
+ | deadline[day] | 1 |
+ | deadline[month] | January |
+ | deadline[year] | 2020 |
+ | deadline[hour] | 08 |
+ | deadline[minute] | 00 |
+ And I press "Save"
+ And I should see "Wednesday, 1 January 2020, 8:00"
+ Then I click on "Edit" "link"
+ And I set the following fields to these values:
+ | deadline[year] | 2030 |
+ And I press "Save"
+ And I should see "Tuesday, 1 January 2030, 8:00"
+ And I click on "Delete" "link"
+ And I press "Continue"
+ And I should not see "Group 1"
+
+ Scenario: Duplicate a user override
+ When I follow "Test lesson name"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ And I press "Add group override"
+ And I set the following fields to these values:
+ | Override group | Group 1 |
+ | id_deadline_enabled | 1 |
+ | deadline[day] | 1 |
+ | deadline[month] | January |
+ | deadline[year] | 2020 |
+ | deadline[hour] | 08 |
+ | deadline[minute] | 00 |
+ And I press "Save"
+ And I should see "Wednesday, 1 January 2020, 8:00"
+ Then I click on "copy" "link"
+ And I set the following fields to these values:
+ | Override group | Group 2 |
+ | deadline[year] | 2030 |
+ And I press "Save"
+ And I should see "Tuesday, 1 January 2030, 8:00"
+ And I should see "Group 2"
+
+ Scenario: Allow a single group to have re-take the lesson
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | Re-takes allowed | 0 |
+ And I press "Save and display"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ And I press "Add group override"
+ And I set the following fields to these values:
+ | Override group | Group 1 |
+ | Re-takes allowed | 1 |
+ And I press "Save"
+ And I should see "Re-takes allowed"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | False | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+ And I follow "Test lesson name"
+ Then I should not see "You are not allowed to retake this lesson."
+ And I should see "Cat is an amphibian"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | False | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+ And I follow "Test lesson name"
+ And I should see "You are not allowed to retake this lesson."
+
+ Scenario: Allow a single group to have a different password
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | Password protected lesson | Yes |
+ | id_password | moodle_rules |
+ And I press "Save and display"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ And I press "Add group override"
+ And I set the following fields to these values:
+ | Override group | Group 1 |
+ | Password protected lesson | 12345 |
+ And I press "Save"
+ And I should see "Password protected lesson"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ Then I should see "Test lesson name is a password protected lesson"
+ And I should not see "Cat is an amphibian"
+ And I set the field "userpassword" to "moodle_rules"
+ And I press "Continue"
+ And I should see "Login failed, please try again..."
+ And I should see "Test lesson name is a password protected lesson"
+ And I set the field "userpassword" to "12345"
+ And I press "Continue"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | False | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Test lesson name is a password protected lesson"
+ And I should not see "Cat is an amphibian"
+ And I set the field "userpassword" to "12345"
+ And I press "Continue"
+ And I should see "Login failed, please try again..."
+ And I should see "Test lesson name is a password protected lesson"
+ And I set the field "userpassword" to "moodle_rules"
+ And I press "Continue"
+
+ Scenario: Allow a group to have a different due date
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | id_deadline_enabled | 1 |
+ | deadline[day] | 1 |
+ | deadline[month] | January |
+ | deadline[year] | 2000 |
+ | deadline[hour] | 08 |
+ | deadline[minute] | 00 |
+ And I press "Save and display"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ And I press "Add group override"
+ And I set the following fields to these values:
+ | Override group | Group 1 |
+ | id_deadline_enabled | 1 |
+ | deadline[day] | 1 |
+ | deadline[month] | January |
+ | deadline[year] | 2020 |
+ | deadline[hour] | 08 |
+ | deadline[minute] | 00 |
+ And I press "Save"
+ And I should see "Lesson closes"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ Then I should see "This lesson closed on Saturday, 1 January 2000, 8:00"
+ And I should not see "Cat is an amphibian"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ And I should see "Cat is an amphibian"
+
+ Scenario: Allow a group to have a different start date
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | id_available_enabled | 1 |
+ | available[day] | 1 |
+ | available[month] | January |
+ | available[year] | 2020 |
+ | available[hour] | 08 |
+ | available[minute] | 00 |
+ And I press "Save and display"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ And I press "Add group override"
+ And I set the following fields to these values:
+ | Override group | Group 1 |
+ | id_available_enabled | 1 |
+ | available[day] | 1 |
+ | available[month] | January |
+ | available[year] | 2015 |
+ | available[hour] | 08 |
+ | available[minute] | 00 |
+ And I press "Save"
+ And I should see "Lesson opens"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ Then I should see "This lesson will be open on Wednesday, 1 January 2020, 8:00"
+ And I should not see "Cat is an amphibian"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ And I should see "Cat is an amphibian"
+
+ Scenario: Allow a single group to have multiple attempts at each question
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | Re-takes allowed | 1 |
+ And I press "Save and display"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ And I press "Add group override"
+ And I set the following fields to these values:
+ | Override group | Group 1 |
+ | Maximum number of attempts | 2 |
+ And I press "Save"
+ And I should see "Maximum number of attempts"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | True | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | True | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | True | 1 |
+ And I press "Submit"
+ Then I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+
+ Scenario: Add both a user and group override and verify that both are applied correctly
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | id_available_enabled | 1 |
+ | available[day] | 1 |
+ | available[month] | January |
+ | available[year] | 2030 |
+ | available[hour] | 08 |
+ | available[minute] | 00 |
+ And I press "Save and display"
+ And I navigate to "Group overrides" node in "Lesson administration"
+ And I press "Add group override"
+ And I set the following fields to these values:
+ | Override group | Group 1 |
+ | id_available_enabled | 1 |
+ | available[day] | 1 |
+ | available[month] | January |
+ | available[year] | 2020 |
+ | available[hour] | 08 |
+ | available[minute] | 00 |
+ And I press "Save"
+ And I should see "Wednesday, 1 January 2020, 8:00"
+ And I navigate to "User overrides" node in "Lesson administration"
+ And I press "Add user override"
+ And I set the following fields to these values:
+ | Override user | Student1 |
+ | id_available_enabled | 1 |
+ | available[day] | 1 |
+ | available[month] | January |
+ | available[year] | 2021 |
+ | available[hour] | 08 |
+ | available[minute] | 00 |
+ And I press "Save"
+ And I should see "Friday, 1 January 2021, 8:00"
+ And I log out
+ Then I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ And I should see "This lesson will be open on Friday, 1 January 2021, 8:00"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ And I should see "This lesson will be open on Tuesday, 1 January 2030, 8:00"
+ And I log out
+ And I log in as "student3"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ And I should see "This lesson will be open on Wednesday, 1 January 2020, 8:00"
--- /dev/null
+@mod @mod_lesson
+Feature: Lesson user override
+ In order to grant a student special access to a lesson
+ As a teacher
+ I need to create an override for that user.
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | teacher1 | Tina | Teacher1 | teacher1@asd.com |
+ | student1 | Sam1 | Student1 | student1@asd.com |
+ | student2 | Sam2 | Student2 | student2@asd.com |
+ And the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ And the following "groups" exist:
+ | name | course | idnumber |
+ | Group 1 | C1 | G1 |
+ | Group 2 | C1 | G2 |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber |
+ | lesson | Test lesson name | Test lesson description | C1 | lesson1 |
+ And I log in as "teacher1"
+ And I follow "Course 1"
+ And I turn editing mode on
+ And I follow "Test lesson name"
+ And I follow "Add a question page"
+ And I set the field "Select a question type" to "True/false"
+ And I press "Add a question page"
+ And I set the following fields to these values:
+ | Page title | True/false question 1 |
+ | Page contents | Cat is an amphibian |
+ | id_answer_editor_0 | False |
+ | id_response_editor_0 | Correct |
+ | id_jumpto_0 | Next page |
+ | id_answer_editor_1 | True |
+ | id_response_editor_1 | Wrong |
+ | id_jumpto_1 | This page |
+ And I press "Save page"
+
+ Scenario: Add, modify then delete a user override
+ When I follow "Test lesson name"
+ And I navigate to "User overrides" node in "Lesson administration"
+ And I press "Add user override"
+ And I set the following fields to these values:
+ | Override user | Student1 |
+ | id_deadline_enabled | 1 |
+ | deadline[day] | 1 |
+ | deadline[month] | January |
+ | deadline[year] | 2020 |
+ | deadline[hour] | 08 |
+ | deadline[minute] | 00 |
+ And I press "Save"
+ And I should see "Wednesday, 1 January 2020, 8:00"
+ Then I click on "Edit" "link"
+ And I set the following fields to these values:
+ | deadline[year] | 2030 |
+ And I press "Save"
+ And I should see "Tuesday, 1 January 2030, 8:00"
+ And I click on "Delete" "link"
+ And I press "Continue"
+ And I should not see "Sam1 Student1"
+
+ Scenario: Duplicate a user override
+ When I follow "Test lesson name"
+ And I navigate to "User overrides" node in "Lesson administration"
+ And I press "Add user override"
+ And I set the following fields to these values:
+ | Override user | Student1 |
+ | id_deadline_enabled | 1 |
+ | deadline[day] | 1 |
+ | deadline[month] | January |
+ | deadline[year] | 2020 |
+ | deadline[hour] | 08 |
+ | deadline[minute] | 00 |
+ And I press "Save"
+ And I should see "Wednesday, 1 January 2020, 8:00"
+ Then I click on "copy" "link"
+ And I set the following fields to these values:
+ | Override user | Student2 |
+ | deadline[year] | 2030 |
+ And I press "Save"
+ And I should see "Tuesday, 1 January 2030, 8:00"
+ And I should see "Sam2 Student2"
+
+ Scenario: Allow a single user to have re-take the lesson
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | Re-takes allowed | 0 |
+ And I press "Save and display"
+ And I navigate to "User overrides" node in "Lesson administration"
+ And I press "Add user override"
+ And I set the following fields to these values:
+ | Override user | Student1 |
+ | Re-takes allowed | 1 |
+ And I press "Save"
+ And I should see "Re-takes allowed"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | False | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+ And I follow "Test lesson name"
+ Then I should not see "You are not allowed to retake this lesson."
+ And I should see "Cat is an amphibian"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | False | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+ And I follow "Test lesson name"
+ And I should see "You are not allowed to retake this lesson."
+
+ Scenario: Allow a single user to have a different password
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | Password protected lesson | Yes |
+ | id_password | moodle_rules |
+ And I press "Save and display"
+ And I navigate to "User overrides" node in "Lesson administration"
+ And I press "Add user override"
+ And I set the following fields to these values:
+ | Override user | Student1 |
+ | Password protected lesson | 12345 |
+ And I press "Save"
+ And I should see "Password protected lesson"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ Then I should see "Test lesson name is a password protected lesson"
+ And I should not see "Cat is an amphibian"
+ And I set the field "userpassword" to "moodle_rules"
+ And I press "Continue"
+ And I should see "Login failed, please try again..."
+ And I should see "Test lesson name is a password protected lesson"
+ And I set the field "userpassword" to "12345"
+ And I press "Continue"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | False | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Test lesson name is a password protected lesson"
+ And I should not see "Cat is an amphibian"
+ And I set the field "userpassword" to "12345"
+ And I press "Continue"
+ And I should see "Login failed, please try again..."
+ And I should see "Test lesson name is a password protected lesson"
+ And I set the field "userpassword" to "moodle_rules"
+ And I press "Continue"
+
+ Scenario: Allow a user to have a different due date
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | id_deadline_enabled | 1 |
+ | deadline[day] | 1 |
+ | deadline[month] | January |
+ | deadline[year] | 2000 |
+ | deadline[hour] | 08 |
+ | deadline[minute] | 00 |
+ And I press "Save and display"
+ And I navigate to "User overrides" node in "Lesson administration"
+ And I press "Add user override"
+ And I set the following fields to these values:
+ | Override user | Student1 |
+ | id_deadline_enabled | 1 |
+ | deadline[day] | 1 |
+ | deadline[month] | January |
+ | deadline[year] | 2020 |
+ | deadline[hour] | 08 |
+ | deadline[minute] | 00 |
+ And I press "Save"
+ And I should see "Lesson closes"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ Then I should see "This lesson closed on Saturday, 1 January 2000, 8:00"
+ And I should not see "Cat is an amphibian"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ And I should see "Cat is an amphibian"
+
+ Scenario: Allow a user to have a different start date
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | id_available_enabled | 1 |
+ | available[day] | 1 |
+ | available[month] | January |
+ | available[year] | 2020 |
+ | available[hour] | 08 |
+ | available[minute] | 00 |
+ And I press "Save and display"
+ And I navigate to "User overrides" node in "Lesson administration"
+ And I press "Add user override"
+ And I set the following fields to these values:
+ | Override user | Student1 |
+ | id_available_enabled | 1 |
+ | available[day] | 1 |
+ | available[month] | January |
+ | available[year] | 2015 |
+ | available[hour] | 08 |
+ | available[minute] | 00 |
+ And I press "Save"
+ And I should see "Lesson opens"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ Then I should see "This lesson will be open on Wednesday, 1 January 2020, 8:00"
+ And I should not see "Cat is an amphibian"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson"
+ And I should see "Cat is an amphibian"
+
+ Scenario: Allow a single user to have multiple attempts at each question
+ When I follow "Test lesson name"
+ And I navigate to "Edit settings" node in "Lesson administration"
+ And I set the following fields to these values:
+ | Re-takes allowed | 1 |
+ And I press "Save and display"
+ And I navigate to "User overrides" node in "Lesson administration"
+ And I press "Add user override"
+ And I set the following fields to these values:
+ | Override user | Student1 |
+ | Maximum number of attempts | 2 |
+ And I press "Save"
+ And I should see "Maximum number of attempts"
+ And I log out
+ And I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | True | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | True | 1 |
+ And I press "Submit"
+ And I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
+ And I log out
+ And I log in as "student2"
+ And I follow "Course 1"
+ And I follow "Test lesson name"
+ And I should see "Cat is an amphibian"
+ And I set the following fields to these values:
+ | True | 1 |
+ And I press "Submit"
+ Then I press "Continue"
+ And I should see "Congratulations - end of lesson reached"
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
+
+ /**
+ * Test the user override created event.
+ *
+ * There is no external API for creating a user override, so the unit test will simply
+ * create and trigger the event and ensure the event data is returned as expected.
+ */
+ public function test_user_override_created() {
+
+ $params = array(
+ 'objectid' => 1,
+ 'relateduserid' => 2,
+ 'context' => context_module::instance($this->lesson->properties()->cmid),
+ 'other' => array(
+ 'lessonid' => $this->lesson->id
+ )
+ );
+ $event = \mod_lesson\event\user_override_created::create($params);
+
+ // Trigger and capture the event.
+ $sink = $this->redirectEvents();
+ $event->trigger();
+ $events = $sink->get_events();
+ $event = reset($events);
+
+ // Check that the event data is valid.
+ $this->assertInstanceOf('\mod_lesson\event\user_override_created', $event);
+ $this->assertEquals(context_module::instance($this->lesson->properties()->cmid), $event->get_context());
+ $this->assertEventContextNotUsed($event);
+ }
+
+ /**
+ * Test the group override created event.
+ *
+ * There is no external API for creating a group override, so the unit test will simply
+ * create and trigger the event and ensure the event data is returned as expected.
+ */
+ public function test_group_override_created() {
+
+ $params = array(
+ 'objectid' => 1,
+ 'context' => context_module::instance($this->lesson->properties()->cmid),
+ 'other' => array(
+ 'lessonid' => $this->lesson->id,
+ 'groupid' => 2
+ )
+ );
+ $event = \mod_lesson\event\group_override_created::create($params);
+
+ // Trigger and capture the event.
+ $sink = $this->redirectEvents();
+ $event->trigger();
+ $events = $sink->get_events();
+ $event = reset($events);
+
+ // Check that the event data is valid.
+ $this->assertInstanceOf('\mod_lesson\event\group_override_created', $event);
+ $this->assertEquals(context_module::instance($this->lesson->properties()->cmid), $event->get_context());
+ $this->assertEventContextNotUsed($event);
+ }
+
+ /**
+ * Test the user override updated event.
+ *
+ * There is no external API for updating a user override, so the unit test will simply
+ * create and trigger the event and ensure the event data is returned as expected.
+ */
+ public function test_user_override_updated() {
+
+ $params = array(
+ 'objectid' => 1,
+ 'relateduserid' => 2,
+ 'context' => context_module::instance($this->lesson->properties()->cmid),
+ 'other' => array(
+ 'lessonid' => $this->lesson->id
+ )
+ );
+ $event = \mod_lesson\event\user_override_updated::create($params);
+
+ // Trigger and capture the event.
+ $sink = $this->redirectEvents();
+ $event->trigger();
+ $events = $sink->get_events();
+ $event = reset($events);
+
+ // Check that the event data is valid.
+ $this->assertInstanceOf('\mod_lesson\event\user_override_updated', $event);
+ $this->assertEquals(context_module::instance($this->lesson->properties()->cmid), $event->get_context());
+ $this->assertEventContextNotUsed($event);
+ }
+
+ /**
+ * Test the group override updated event.
+ *
+ * There is no external API for updating a group override, so the unit test will simply
+ * create and trigger the event and ensure the event data is returned as expected.
+ */
+ public function test_group_override_updated() {
+
+ $params = array(
+ 'objectid' => 1,
+ 'context' => context_module::instance($this->lesson->properties()->cmid),
+ 'other' => array(
+ 'lessonid' => $this->lesson->id,
+ 'groupid' => 2
+ )
+ );
+ $event = \mod_lesson\event\group_override_updated::create($params);
+
+ // Trigger and capture the event.
+ $sink = $this->redirectEvents();
+ $event->trigger();
+ $events = $sink->get_events();
+ $event = reset($events);
+
+ // Check that the event data is valid.
+ $this->assertInstanceOf('\mod_lesson\event\group_override_updated', $event);
+ $this->assertEquals(context_module::instance($this->lesson->properties()->cmid), $event->get_context());
+ $this->assertEventContextNotUsed($event);
+ }
+
+ /**
+ * Test the user override deleted event.
+ */
+ public function test_user_override_deleted() {
+ global $DB;
+
+ // Create an override.
+ $override = new stdClass();
+ $override->lesson = $this->lesson->id;
+ $override->userid = 2;
+ $override->id = $DB->insert_record('lesson_overrides', $override);
+
+ // Trigger and capture the event.
+ $sink = $this->redirectEvents();
+ $this->lesson->delete_override($override->id);
+ $events = $sink->get_events();
+ $event = reset($events);
+
+ // Check that the event data is valid.
+ $this->assertInstanceOf('\mod_lesson\event\user_override_deleted', $event);
+ $this->assertEquals(context_module::instance($this->lesson->properties()->cmid), $event->get_context());
+ $this->assertEventContextNotUsed($event);
+ }
+
+ /**
+ * Test the group override deleted event.
+ */
+ public function test_group_override_deleted() {
+ global $DB;
+
+ // Create an override.
+ $override = new stdClass();
+ $override->lesson = $this->lesson->id;
+ $override->groupid = 2;
+ $override->id = $DB->insert_record('lesson_overrides', $override);
+
+ // Trigger and capture the event.
+ $sink = $this->redirectEvents();
+ $this->lesson->delete_override($override->id);
+ $events = $sink->get_events();
+ $event = reset($events);
+
+ // Check that the event data is valid.
+ $this->assertInstanceOf('\mod_lesson\event\group_override_deleted', $event);
+ $this->assertEquals(context_module::instance($this->lesson->properties()->cmid), $event->get_context());
+ $this->assertEventContextNotUsed($event);
+ }
}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2015032700; // The current module version (Date: YYYYMMDDXX)
+$plugin->version = 2015033100; // The current module version (Date: YYYYMMDDXX)
$plugin->requires = 2014110400; // Requires this Moodle version
$plugin->component = 'mod_lesson'; // Full name of the plugin (used for diagnostics)
$plugin->cron = 0;
redirect(new moodle_url('/course/view.php', array('id'=>$course->id)));
}
+// Apply overrides.
+$lesson->update_effective_access($USER->id);
+
// Mark as viewed
$completion = new completion_info($course);
$completion->set_module_viewed($cm);
$correctpass = false;
if (!empty($userpassword) && (($lesson->password == md5(trim($userpassword))) || ($lesson->password == trim($userpassword)))) {
// with or without md5 for backward compatibility (MDL-11090)
+ $correctpass = true;
$USER->lessonloggedin[$lesson->id] = true;
if ($lesson->highscores) {
// Logged in - redirect so we go through all of these checks before starting the lesson.
redirect("$CFG->wwwroot/mod/lesson/view.php?id=$cm->id");
}
- } else {
+ } 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) {
+ $correctpass = true;
+ $USER->lessonloggedin[$lesson->id] = true;
+ if ($lesson->highscores) {
+ // Logged in - redirect so we go through all of these checks before starting the lesson.
+ redirect("$CFG->wwwroot/mod/lesson/view.php?id=$cm->id");
+ }
+ }
+ }
+ }
+ 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();
);
}
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.9
+ */
+ public static function view_user_list_parameters() {
+ return new external_function_parameters(
+ array(
+ 'courseid' => new external_value(PARAM_INT, 'id of the course, 0 for site')
+ )
+ );
+ }
+
+ /**
+ * Simulate the /user/index.php web interface page triggering events
+ *
+ * @param int $courseid id of course
+ * @return array of warnings and status result
+ * @since Moodle 2.9
+ * @throws moodle_exception
+ */
+ public static function view_user_list($courseid) {
+ global $CFG;
+ require_once($CFG->dirroot . "/user/lib.php");
+
+ $params = self::validate_parameters(self::view_user_list_parameters(),
+ array(
+ 'courseid' => $courseid
+ ));
+
+ $warnings = array();
+
+ if (empty($params['courseid'])) {
+ $params['courseid'] = SITEID;
+ }
+
+ $course = get_course($params['courseid']);
+
+ if ($course->id == SITEID) {
+ $context = context_system::instance();
+ } else {
+ $context = context_course::instance($course->id);
+ }
+ self::validate_context($context);
+
+ if ($course->id == SITEID) {
+ require_capability('moodle/site:viewparticipants', $context);
+ } else {
+ require_capability('moodle/course:viewparticipants', $context);
+ }
+
+ user_list_view($course, $context);
+
+ $result = array();
+ $result['status'] = true;
+ $result['warnings'] = $warnings;
+ return $result;
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.9
+ */
+ public static function view_user_list_returns() {
+ return new external_single_structure(
+ array(
+ 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+ 'warnings' => new external_warnings()
+ )
+ );
+ }
+
}
/**
*/
require_once('../config.php');
+require_once($CFG->dirroot.'/user/lib.php');
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->libdir.'/filelib.php');
}
}
-$event = \core\event\user_list_viewed::create(array(
- 'objectid' => $course->id,
- 'courseid' => $course->id,
- 'context' => $context,
- 'other' => array(
- 'courseshortname' => $course->shortname,
- 'coursefullname' => $course->fullname
- )
-));
-$event->trigger();
+// Trigger events.
+user_list_view($course, $context);
$bulkoperations = has_capability('moodle/course:bulkmessaging', $context);
return true;
}
+
+/**
+ * Trigger user_list_viewed event.
+ *
+ * @param stdClass $course course object
+ * @param stdClass $context course context object
+ * @since Moodle 2.9
+ */
+function user_list_view($course, $context) {
+
+ $event = \core\event\user_list_viewed::create(array(
+ 'objectid' => $course->id,
+ 'courseid' => $course->id,
+ 'context' => $context,
+ 'other' => array(
+ 'courseshortname' => $course->shortname,
+ 'coursefullname' => $course->fullname
+ )
+ ));
+ $event->trigger();
+}
$this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
$this->assertEquals(0, $DB->count_records('user_password_history', array('userid' => $user2->id)));
}
+
+ /**
+ * Test user_list_view function
+ */
+ public function test_user_list_view() {
+
+ $this->resetAfterTest();
+
+ // Course without sections.
+ $course = $this->getDataGenerator()->create_course();
+ $context = context_course::instance($course->id);
+
+ $this->setAdminUser();
+
+ // Redirect events to the sink, so we can recover them later.
+ $sink = $this->redirectEvents();
+
+ user_list_view($course, $context);
+ $events = $sink->get_events();
+ $this->assertCount(1, $events);
+ $event = reset($events);
+
+ // Check the event details are correct.
+ $this->assertInstanceOf('\core\event\user_list_viewed', $event);
+ $this->assertEquals($context, $event->get_context());
+ $this->assertEquals($course->shortname, $event->other['courseshortname']);
+ $this->assertEquals($course->fullname, $event->other['coursefullname']);
+
+ }
+
}
defined('MOODLE_INTERNAL') || die();
-$version = 2015040600.00; // YYYYMMDD = weekly release date of this DEV branch.
+$version = 2015040600.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.