MDL-49642 mod_lesson: Group/user overrides

This commit is contained in:
Jean-Michel Vedrine
2015-04-02 13:56:52 +02:00
parent 1d3fd63f97
commit e0e1a83e2c
28 changed files with 2474 additions and 50 deletions
+3
View File
@@ -233,6 +233,9 @@ class moodle1_mod_lesson_handler extends moodle1_mod_handler {
* 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');
@@ -145,6 +145,11 @@ class backup_lesson_activity_structure_step extends backup_activity_structure_st
'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);
@@ -161,6 +166,8 @@ class backup_lesson_activity_structure_step extends backup_activity_structure_st
$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)
@@ -171,6 +178,9 @@ class backup_lesson_activity_structure_step extends backup_activity_structure_st
// 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
@@ -180,7 +190,9 @@ class backup_lesson_activity_structure_step extends backup_activity_structure_st
$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');
@@ -188,6 +200,8 @@ class backup_lesson_activity_structure_step extends backup_activity_structure_st
$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);
@@ -48,6 +48,7 @@ class restore_lesson_activity_structure_step extends restore_activity_structure_
$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
@@ -204,6 +205,39 @@ class restore_lesson_activity_structure_step extends restore_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;
@@ -0,0 +1,100 @@
<?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.');
}
}
}
@@ -0,0 +1,99 @@
<?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.');
}
}
}
@@ -0,0 +1,99 @@
<?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.');
}
}
}
@@ -0,0 +1,98 @@
<?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.');
}
}
}
@@ -0,0 +1,98 @@
<?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.');
}
}
}
@@ -0,0 +1,99 @@
<?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.');
}
}
}
+85
View File
@@ -0,0 +1,85 @@
<?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);
}
}
+3
View File
@@ -36,6 +36,9 @@ $lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*'
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');
+11 -1
View File
@@ -85,5 +85,15 @@ $capabilities = array(
'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
)
),
);
+43
View File
@@ -0,0 +1,43 @@
<?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'
),
);
+21
View File
@@ -192,5 +192,26 @@
<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>
+33
View File
@@ -263,5 +263,38 @@ function xmldb_lesson_upgrade($oldversion) {
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;
}
+2
View File
@@ -62,6 +62,8 @@ if ($attemptid > 0) {
$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;
+34
View File
@@ -48,6 +48,8 @@ $string['addedaquestionpage'] = 'Added a question page';
$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';
@@ -87,6 +89,7 @@ $string['casesensitive_help'] = 'Tick the checkbox to use regular expressions fo
$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';
@@ -136,6 +139,7 @@ $string['description'] = 'Description';
$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';
@@ -154,12 +158,14 @@ $string['displayscorewithoutessays'] = 'Your score is {$a->score} (out of {$a->g
$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';
@@ -185,6 +191,9 @@ $string['eventlessonended'] = 'Lesson ended';
$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';
@@ -208,6 +217,9 @@ $string['gradebetterthanerror'] = 'Earn a grade better than {$a} percent';
$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.';
@@ -226,6 +238,7 @@ $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';
@@ -243,6 +256,7 @@ $string['lessoncloseson'] = 'Lesson closes on {$a}';
$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}.';
@@ -310,6 +324,7 @@ $string['nextpage'] = 'Next page';
$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';
@@ -318,12 +333,15 @@ $string['noessayquestionsfound'] = 'No essay questions found in this lesson.';
$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';
@@ -351,6 +369,14 @@ $string['or'] = 'OR';
$string['ordered'] = 'Ordered';
$string['other'] = 'Other';
$string['outof'] = 'Out of {$a}';
$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['override'] = 'Override';
$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}';
@@ -398,6 +424,8 @@ $string['rank'] = 'Rank';
$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';
@@ -405,13 +433,16 @@ $string['retakesallowed'] = 'Re-takes allowed';
$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).';
@@ -464,6 +495,9 @@ $string['usemaximum'] = 'Use maximum';
$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['useroverridesdeleted'] = 'User overrides deleted';
$string['usersnone'] = 'No students have access to this lesson';
$string['useroverrides'] = 'User overrides';
$string['viewgrades'] = 'View grades';
$string['viewhighscores'] = 'View high scores list';
$string['viewreports'] = 'View {$a->attempts} completed {$a->student} attempts';
+220 -45
View File
@@ -95,6 +95,166 @@ function lesson_update_instance($data, $mform) {
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,
@@ -572,50 +732,8 @@ function lesson_process_pre_save(&$lesson) {
* @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);
}
@@ -628,6 +746,10 @@ function lesson_process_post_save(&$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'));
}
/**
@@ -636,7 +758,9 @@ function lesson_reset_course_form_definition(&$mform) {
* @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);
}
/**
@@ -710,8 +834,35 @@ function lesson_reset_userdata($data) {
$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);
}
@@ -820,6 +971,30 @@ function lesson_get_completion_state($course, $cm, $userid, $type) {
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);
+226 -1
View File
@@ -638,6 +638,31 @@ function lesson_get_media_html($lesson, $context) {
return $code;
}
/**
* Logic to happen when a/some group(s) has/have been deleted in a course.
*
* @param int $courseid The course ID.
* @return void
*/
function lesson_process_group_deleted_in_course($courseid) {
global $DB;
// It would be nice if we got the groupid that was deleted.
// Instead, we just update all lesson with orphaned group overrides.
$sql = "SELECT o.id, o.lesson
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";
$params = array('courseid' => $courseid);
$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.
@@ -988,6 +1013,8 @@ class lesson extends lesson_base {
$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));
@@ -1011,6 +1038,204 @@ class lesson extends lesson_base {
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.
@@ -1452,7 +1677,7 @@ class lesson extends lesson_base {
* @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);
+19 -1
View File
@@ -38,6 +38,10 @@ $lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*'
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);
@@ -86,11 +90,25 @@ if (!$canmanage) {
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();
+287
View File
@@ -0,0 +1,287 @@
<?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 = array();
list($sort, $sortparams) = users_order_by_sql('u');
if (!empty($sortparams)) {
throw new coding_exception('users_order_by_sql returned some query parameters. ' .
'This is unexpected, and a problem because there is no way to pass these ' .
'parameters to get_users_by_capability. See MDL-34657.');
}
$users = get_enrolled_users($this->context, '', 0,
'u.id, u.email, ' . get_all_user_name_fields(true, 'u'),
$sort);
// 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;
}
}
+94
View File
@@ -0,0 +1,94 @@
<?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();
+224
View File
@@ -0,0 +1,224 @@
<?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};
}
}
// If we are duplicating an override, then clear the user/group and override id
// since they will change.
if ($action === 'duplicate') {
$override->id = null;
$override->userid = null;
$override->groupid = null;
}
// True if group-based override.
$groupmode = !empty($data->groupid) || ($action === 'addgroup' && empty($overrideid));
$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();
+295
View File
@@ -0,0 +1,295 @@
<?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();
+5
View File
@@ -410,6 +410,11 @@ if ($action === 'delete') {
$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) {
+209
View File
@@ -502,4 +502,213 @@ class mod_lesson_events_testcase extends advanced_testcase {
$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() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
$params = array(
'objectid' => 1,
'context' => context_module::instance($lesson->cmid),
'other' => array(
'lessonid' => $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($lesson->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() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
$params = array(
'objectid' => 1,
'context' => context_module::instance($lesson->cmid),
'other' => array(
'lessonid' => $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($lesson->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() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
$params = array(
'objectid' => 1,
'relateduserid' => 2,
'context' => context_module::instance($lesson->cmid),
'other' => array(
'lessonid' => $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($lesson->cmid), $event->get_context());
$expected = array($course->id, 'lesson', 'edit override', 'overrideedit.php?id=1', $lesson->id, $lesson->cmid);
$this->assertEventLegacyLogData($expected, $event);
$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() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
$params = array(
'objectid' => 1,
'context' => context_module::instance($lesson->cmid),
'other' => array(
'lessonid' => $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($lesson->cmid), $event->get_context());
$expected = array($course->id, 'lesson', 'edit override', 'overrideedit.php?id=1', $lesson->id, $lesson->cmid);
$this->assertEventLegacyLogData($expected, $event);
$this->assertEventContextNotUsed($event);
}
/**
* Test the user override deleted event.
*/
public function test_user_override_deleted() {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
// Create an override.
$override = new stdClass();
$override->lesson = $lesson->id;
$override->userid = 2;
$override->id = $DB->insert_record('lesson_overrides', $override);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$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($lesson->cmid), $event->get_context());
$expected = array($course->id, 'lesson', 'delete override',
'overrides.php?cmid=' . $lesson->cmid, $lesson->id, $lesson->cmid);
$this->assertEventLegacyLogData($expected, $event);
$this->assertEventContextNotUsed($event);
}
/**
* Test the group override deleted event.
*/
public function test_group_override_deleted() {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
// Create an override.
$override = new stdClass();
$override->lesson = $lesson->id;
$override->groupid = 2;
$override->id = $DB->insert_record('lesson_overrides', $override);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$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($lesson->cmid), $event->get_context());
$expected = array($course->id, 'lesson', 'delete override',
'overrides.php?cmid=' . $lesson->cmid, $lesson->id, $lesson->cmid);
$this->assertEventLegacyLogData($expected, $event);
$this->assertEventContextNotUsed($event);
}
}
+1 -1
View File
@@ -24,7 +24,7 @@
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;
+18 -1
View File
@@ -45,6 +45,9 @@ if ($backtocourse) {
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);
@@ -85,12 +88,26 @@ if (!$canmanage) {
$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();