Skip to content

Commit

Permalink
MDL-24419 (4): Add conditional availability support to sections; sect…
Browse files Browse the repository at this point in the history
…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
sammarshallou committed May 14, 2012
1 parent 637da99 commit ce4dfd2
Show file tree
Hide file tree
Showing 20 changed files with 1,625 additions and 259 deletions.
10 changes: 8 additions & 2 deletions backup/moodle2/backup_stepslib.php
Expand Up @@ -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');
Expand Down
57 changes: 56 additions & 1 deletion backup/moodle2/restore_stepslib.php
Expand Up @@ -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) {
Expand All @@ -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;

Expand All @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions course/edit.php
Expand Up @@ -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':
Expand Down
30 changes: 29 additions & 1 deletion course/editsection.php
Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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);
Expand Down
152 changes: 149 additions & 3 deletions course/editsection_form.php
Expand Up @@ -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);

Expand All @@ -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;
}
}

0 comments on commit ce4dfd2

Please sign in to comment.