Permalink
Browse files

MDL-24419 (4): Add conditional availability support to sections; sect…

…ion cache

Credit: original version done by Kirill Astashov of NetSpot (netspot.com.au),
finished and tweaked by sam.

This change adds conditional availability support for sections analagous to
that already available for activities. (Backend, UI, backup/restore.)

In order that this feature does not reduce performance, section cacheing has
also been added using a new course 'sectioncache' field analagous to modinfo.

The new feature integrates with activity availability so that activities
inside sections which are not available are automatically not available
themselves (meaning it works to restrict access).
  • Loading branch information...
1 parent 637da99 commit ce4dfd2703fcd4eb0c80853c631acd2f67299f01 @sammarshallou sammarshallou committed Apr 2, 2012
@@ -364,14 +364,20 @@ protected function define_structure() {
// Define each element separated
$section = new backup_nested_element('section', array('id'), array(
- 'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible'));
+ 'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible',
+ 'availablefrom', 'availableuntil', 'showavailability', 'groupingid'));
// attach format plugin structure to $section element, only one allowed
$this->add_plugin_structure('format', $section, false);
- // Define sources
+ // Add nested elements for _availability table
+ $avail = new backup_nested_element('availability', array('id'), array(
+ 'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
+ $section->add_child($avail);
+ // Define sources
$section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
+ $avail->set_source_table('course_sections_availability', array('coursesectionid' => backup::VAR_SECTIONID));
// Aliases
$section->set_source_alias('section', 'number');
@@ -993,11 +993,54 @@ class restore_section_structure_step extends restore_structure_step {
protected function define_structure() {
$section = new restore_path_element('section', '/section');
+ $avail = new restore_path_element('availability', '/section/availability');
// Apply for 'format' plugins optional paths at section level
$this->add_plugin_structure('format', $section);
- return array($section);
+ return array($section, $avail);
+ }
+
+ public function process_availability($data) {
+ global $DB;
+ $data = (object)$data;
+ $data->coursesectionid = $this->task->get_sectionid();
+ // NOTE: Other values in $data need updating, but these (cm,
+ // grade items) have not yet been restored.
+ $DB->insert_record('course_sections_availability', $data);
+ }
+
+ public function after_restore() {
+ global $DB;
+ // Get main data object
+ $sectionid = $this->get_task()->get_sectionid();
+ $data = $DB->get_record('course_sections',
+ array('id' => $sectionid), 'id, groupingid', MUST_EXIST);
+ if ($data->groupingid) {
+ // Correct grouping id
+ $DB->set_field('course_sections', 'groupingid',
+ $this->get_mappingid('grouping', $data->groupingid),
+ array('id' => $sectionid));
+ }
+
+ // Get data object for current section availability (if any)
+ $data = $DB->get_record('course_sections_availability',
+ array('coursesectionid' => $sectionid), 'id, sourcecmid, gradeitemid', IGNORE_MISSING);
+
+ // Update mappings
+ if ($data) {
+ $data->sourcecmid = $this->get_mappingid('course_module', $data->sourcecmid);
+ if (!$data->sourcecmid) {
+ $data->sourcecmid = null;
+ }
+ $data->gradeitemid = $this->get_mappingid('grade_item', $data->gradeitemid);
+ if (!$data->gradeitemid) {
+ $data->gradeitemid = null;
+ }
+
+ $DB->update_record('course_sections_availability', $data);
+ rebuild_course_cache($this->get_task()->get_courseid(), true);
+ }
}
public function process_section($data) {
@@ -1018,6 +1061,10 @@ public function process_section($data) {
$section->summaryformat = $data->summaryformat;
$section->sequence = '';
$section->visible = $data->visible;
+ $section->availablefrom = isset($data->availablefrom) ? $data->availablefrom : 0;
+ $section->availableuntil = isset($data->availableuntil) ? $data->availableuntil : 0;
+ $section->showavailability = isset($data->showavailability) ? $data->showavailability : 0;
+ $section->groupingid = isset($data->groupingid) ? $data->groupingid : 0;
$newitemid = $DB->insert_record('course_sections', $section);
$restorefiles = true;
@@ -1032,6 +1079,14 @@ public function process_section($data) {
$section->summaryformat = $data->summaryformat;
$restorefiles = true;
}
+
+ // Don't update available from, available until, or show availability
+ // (I didn't see a useful way to define whether existing or new one should
+ // take precedence).
+
+ // Always update groupingid (otherwise it will break later when it updates id)
+ $section->groupingid = isset($data->groupingid) ? $data->groupingid : 0;
+
$DB->update_record('course_sections', $section);
$newitemid = $secrec->id;
}
View
@@ -126,6 +126,7 @@
// Save any changes to the files used in the editor
update_course($data, $editoroptions);
}
+ rebuild_course_cache($course->id);
switch ($returnto) {
case 'category':
@@ -26,6 +26,10 @@
require_once("../config.php");
require_once("lib.php");
require_once($CFG->libdir.'/filelib.php');
+require_once($CFG->libdir . '/gradelib.php');
+require_once($CFG->libdir . '/completionlib.php');
+require_once($CFG->libdir . '/conditionlib.php');
+
require_once('editsection_form.php');
$id = required_param('id',PARAM_INT); // Week/topic ID
@@ -43,7 +47,17 @@
$editoroptions = array('context'=>$context ,'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>false, 'noclean'=>true);
$section = file_prepare_standard_editor($section, 'summary', $editoroptions, $context, 'course', 'section', $section->id);
$section->usedefaultname = (is_null($section->name));
-$mform = new editsection_form($PAGE->url, array('course'=>$course, 'editoroptions'=>$editoroptions));
+
+if (!empty($CFG->enableavailability)) {
+ // Get section availability conditions from sectioncache.
+ $modinfo = get_fast_modinfo($course);
+ $sectioninfo = $modinfo->get_section_info($section->section);
+ $section->conditionsgrade = $sectioninfo->conditionsgrade;
+ $section->conditionscompletion = $sectioninfo->conditionscompletion;
+}
+
+$mform = new editsection_form($PAGE->url, array('course' => $course, 'editoroptions' => $editoroptions,
+ 'cs' => $section, 'showavailability' => $section->showavailability));
$mform->set_data($section); // set current value
if ($sectionreturn) {
@@ -65,7 +79,21 @@
$data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $context, 'course', 'section', $section->id);
$section->summary = $data->summary;
$section->summaryformat = $data->summaryformat;
+ if (!empty($CFG->enableavailability)) {
+ $section->availablefrom = $data->availablefrom;
+ $section->availableuntil = $data->availableuntil;
+ if (!empty($data->groupingid)) {
+ $section->groupingid = $data->groupingid;
+ }
+ $section->showavailability = $data->showavailability;
+ }
$DB->update_record('course_sections', $section);
+ if (!empty($CFG->enableavailability)) {
+ // Update grade and completion conditions
+ condition_info_section::update_section_from_form($section, $data);
+ }
+ rebuild_course_cache($course->id);
+
add_to_log($course->id, "course", "editsection", "editsection.php?id=$section->id", "$section->section");
$PAGE->navigation->clear_cache();
redirect($returnurl);
@@ -9,11 +9,9 @@
class editsection_form extends moodleform {
function definition() {
- global $CFG, $DB;
$mform = $this->_form;
$course = $this->_customdata['course'];
-
$mform->addElement('checkbox', 'usedefaultname', get_string('sectionusedefaultname'));
$mform->setDefault('usedefaultname', true);
@@ -30,8 +28,156 @@ function definition() {
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
-//--------------------------------------------------------------------------------
+ $mform->_registerCancelButton('cancel');
+ }
+
+ public function definition_after_data() {
+ global $CFG, $DB;
+
+ $mform = $this->_form;
+ $course = $this->_customdata['course'];
+
+ if (!empty($CFG->enableavailability)) {
+ // Grouping conditions - only if grouping is enabled at site level
+ if (!empty($CFG->enablegroupmembersonly)) {
+ $options = array();
+ $options[0] = get_string('none');
+ if ($groupings = $DB->get_records('groupings', array('courseid' => $course->id))) {
+ foreach ($groupings as $grouping) {
+ $context = context_course::instance($course->id);
+ $options[$grouping->id] = format_string(
+ $grouping->name, true, array('context' => $context));
+ }
+ }
+ $mform->addElement('header', '', get_string('availabilityconditions', 'condition'));
+ $mform->addElement('select', 'groupingid', get_string('groupingsection', 'group'), $options);
+ $mform->addHelpButton('groupingid', 'groupingsection', 'group');
+ }
+
+ // Date and time conditions
+ $mform->addElement('date_time_selector', 'availablefrom',
+ get_string('availablefrom', 'condition'), array('optional' => true));
+ $mform->addElement('date_time_selector', 'availableuntil',
+ get_string('availableuntil', 'condition'), array('optional' => true));
+
+ // Conditions based on grades
+ $gradeoptions = array();
+ $items = grade_item::fetch_all(array('courseid' => $course->id));
+ $items = $items ? $items : array();
+ foreach ($items as $id => $item) {
+ $gradeoptions[$id] = $item->get_name();
+ }
+ asort($gradeoptions);
+ $gradeoptions = array(0 => get_string('none', 'condition')) + $gradeoptions;
+
+ $grouparray = array();
+ $grouparray[] = $mform->createElement('select', 'conditiongradeitemid', '', $gradeoptions);
+ $grouparray[] = $mform->createElement('static', '', '',
+ ' ' . get_string('grade_atleast', 'condition').' ');
+ $grouparray[] = $mform->createElement('text', 'conditiongrademin', '', array('size' => 3));
+ $grouparray[] = $mform->createElement('static', '', '',
+ '% ' . get_string('grade_upto', 'condition') . ' ');
+ $grouparray[] = $mform->createElement('text', 'conditiongrademax', '', array('size' => 3));
+ $grouparray[] = $mform->createElement('static', '', '', '%');
+ $group = $mform->createElement('group', 'conditiongradegroup',
+ get_string('gradecondition', 'condition'), $grouparray);
+
+ // Get full version (including condition info) of section object
+ $ci = new condition_info_section($this->_customdata['cs']);
+ $fullcs = $ci->get_full_section();
+ $count = count($fullcs->conditionsgrade) + 1;
+
+ // Grade conditions
+ $this->repeat_elements(array($group), $count, array(), 'conditiongraderepeats',
+ 'conditiongradeadds', 2, get_string('addgrades', 'condition'), true);
+ $mform->addHelpButton('conditiongradegroup[0]', 'gradecondition', 'condition');
+
+ // Conditions based on completion
+ $completion = new completion_info($course);
+ if ($completion->is_enabled()) {
+ $completionoptions = array();
+ $modinfo = get_fast_modinfo($course);
+ foreach($modinfo->cms as $id => $cm) {
+ // Add each course-module if it:
+ // (a) has completion turned on
+ // (b) does not belong to current course-section
+ if ($cm->completion && ($fullcs->id != $cm->section)) {
+ $completionoptions[$id] = $cm->name;
+ }
+ }
+ asort($completionoptions);
+ $completionoptions = array(0 => get_string('none', 'condition')) +
+ $completionoptions;
+
+ $completionvalues = array(
+ COMPLETION_COMPLETE => get_string('completion_complete', 'condition'),
+ COMPLETION_INCOMPLETE => get_string('completion_incomplete', 'condition'),
+ COMPLETION_COMPLETE_PASS => get_string('completion_pass', 'condition'),
+ COMPLETION_COMPLETE_FAIL => get_string('completion_fail', 'condition'));
+
+ $grouparray = array();
+ $grouparray[] = $mform->createElement('select', 'conditionsourcecmid', '',
+ $completionoptions);
+ $grouparray[] = $mform->createElement('select', 'conditionrequiredcompletion', '',
+ $completionvalues);
+ $group = $mform->createElement('group', 'conditioncompletiongroup',
+ get_string('completioncondition', 'condition'), $grouparray);
+
+ $count = count($fullcs->conditionscompletion) + 1;
+ $this->repeat_elements(array($group), $count,array(),
+ 'conditioncompletionrepeats', 'conditioncompletionadds', 2,
+ get_string('addcompletions', 'condition'), true);
+ $mform->addHelpButton('conditioncompletiongroup[0]',
+ 'completionconditionsection', 'condition');
+ }
+
+ // Availability conditions - set up form values
+ if (!empty($CFG->enableavailability)) {
+ $num = 0;
+ foreach ($fullcs->conditionsgrade as $gradeitemid => $minmax) {
+ $groupelements = $mform->getElement(
+ 'conditiongradegroup[' . $num . ']')->getElements();
+ $groupelements[0]->setValue($gradeitemid);
+ $groupelements[2]->setValue(is_null($minmax->min) ? '' :
+ format_float($minmax->min, 5, true, true));
+ $groupelements[4]->setValue(is_null($minmax->max) ? '' :
+ format_float($minmax->max, 5, true, true));
+ $num++;
+ }
+
+ if ($completion->is_enabled()) {
+ $num = 0;
+ foreach($fullcs->conditionscompletion as $othercmid => $state) {
+ $groupelements = $mform->getElement('conditioncompletiongroup[' . $num . ']')->getElements();
+ $groupelements[0]->setValue($othercmid);
+ $groupelements[1]->setValue($state);
+ $num++;
+ }
+ }
+ }
+
+ // Do we display availability info to students?
+ $showhide = array(
+ CONDITION_STUDENTVIEW_SHOW => get_string('showavailabilitysection_show', 'condition'),
+ CONDITION_STUDENTVIEW_HIDE => get_string('showavailabilitysection_hide', 'condition'));
+ $mform->addElement('select', 'showavailability',
+ get_string('showavailabilitysection', 'condition'), $showhide);
+
+ $mform->setDefault('showavailability', $this->_customdata['showavailability']);
+ }
+
$this->add_action_buttons();
+ }
+
+ public function validation($data, $files) {
+ $errors = parent::validation($data, $files);
+ // Conditions: Don't let them set dates which make no sense
+ if (array_key_exists('availablefrom', $data) &&
+ $data['availablefrom'] && $data['availableuntil'] &&
+ $data['availablefrom'] > $data['availableuntil']) {
+ $errors['availablefrom'] = get_string('badavailabledates', 'condition');
+ }
+ return $errors;
}
}
Oops, something went wrong.

0 comments on commit ce4dfd2

Please sign in to comment.