MDL-15499: Conditional availability of activities

This commit is contained in:
sam_marshall 2008-12-17 16:37:35 +00:00
parent 5373887a7c
commit 82bd6a5ea9
23 changed files with 1551 additions and 97 deletions

View File

@ -14,11 +14,6 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
$item->set_updatedcallback('reset_text_filters_cache');
$temp->add($item);
// Completion system
require_once($CFG->libdir.'/completionlib.php');
$temp->add(new admin_setting_configcheckbox('enablecompletion', get_string('enablecompletion','completion'), get_string('configenablecompletion','completion'), COMPLETION_DISABLED));
$temp->add(new admin_setting_pickroles('progresstrackedroles', get_string('progresstrackedroles','completion'), get_string('configprogresstrackedroles', 'completion'), array('moodle/legacy:student')));
$ADMIN->add('experimental', $temp);
// DB transfer related pages

View File

@ -29,4 +29,15 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
$options = array('off'=>get_string('off', 'mnet'), 'strict'=>get_string('on', 'mnet'));
$optionalsubsystems->add(new admin_setting_configselect('mnet_dispatcher_mode', get_string('net', 'mnet'), get_string('configmnet', 'mnet'), 'off', $options));
// Conditional activities: completion and availability
$optionalsubsystems->add(new admin_setting_configcheckbox('enablecompletion',
get_string('enablecompletion','completion'),
get_string('configenablecompletion','completion'), 0));
$optionalsubsystems->add(new admin_setting_pickroles('progresstrackedroles',
get_string('progresstrackedroles','completion'),
get_string('configprogresstrackedroles', 'completion'),
array('moodle/legacy:student')));
$optionalsubsystems->add(new admin_setting_configcheckbox('enableavailability',
get_string('enableavailability','condition'),
get_string('configenableavailability','condition'), 0));
}

View File

@ -1227,6 +1227,10 @@
fwrite ($bf,full_tag("COMPLETIONGRADEITEMNUMBER",6,false,$course_module->completiongradeitemnumber));
fwrite ($bf,full_tag("COMPLETIONVIEW",6,false,$course_module->completionview));
fwrite ($bf,full_tag("COMPLETIONEXPECTED",6,false,$course_module->completionexpected));
fwrite ($bf,full_tag("AVAILABLEFROM",6,false,$course_module->availablefrom));
fwrite ($bf,full_tag("AVAILABLEUNTIL",6,false,$course_module->availableuntil));
fwrite ($bf,full_tag("SHOWAVAILABILITY",6,false,$course_module->showavailability));
// get all the role_capabilities overrides in this mod
write_role_overrides_xml($bf, $context, 6);
/// write role_assign code here
@ -1254,6 +1258,27 @@
fwrite ($bf,end_tag("COMPLETIONDATA",6,true));
}
// Write availability data if enabled
require_once($CFG->libdir.'/conditionlib.php');
if(!empty($CFG->enableavailability)) {
fwrite ($bf,start_tag("AVAILABILITYDATA",6,true));
// Get all availability restrictions for this activity
$data=$DB->get_records('course_modules_availability',
array('coursemoduleid'=>$course_module->id));
$data=$data ? $data : array();
foreach($data as $availability) {
// Write availability record
fwrite ($bf,start_tag("AVAILABILITY",7,true));
fwrite ($bf,full_tag("SOURCECMID",8,false,$availability->sourcecmid));
fwrite ($bf,full_tag("REQUIREDCOMPLETION",8,false,$availability->requiredcompletion));
fwrite ($bf,full_tag("GRADEITEMID",8,false,$availability->gradeitemid));
fwrite ($bf,full_tag("GRADEMIN",8,false,$availability->grademin));
fwrite ($bf,full_tag("GRADEMAX",8,false,$availability->grademax));
fwrite ($bf,end_tag("AVAILABILITY",7,true));
}
fwrite ($bf,end_tag("AVAILABILITYDATA",6,true));
}
fwrite ($bf,end_tag("MOD",5,true));
}
//check for next

View File

@ -1127,6 +1127,10 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
$course_module->completionview=$mod->completionview;
$course_module->completionexpected=$mod->completionexpected;
$course_module->availablefrom=$mod->availablefrom+$restore->course_startdateoffset;
$course_module->availableuntil=$mod->availableuntil+$restore->course_startdateoffset;
$course_module->showavailability=$mod->showavailability;
$newidmod = $DB->insert_record("course_modules", $course_module);
if ($newidmod) {
//save old and new module id
@ -1221,6 +1225,61 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
}
}
// Store availability information
if($status && !empty($info->availabilitydata) && count($info->availabilitydata)>0) {
foreach($info->availabilitydata as $data) {
// Convert cmid
$newcmid=backup_getid($restore->backup_unique_code, 'course_modules', $data->coursemoduleid);
if($newcmid) {
$data->coursemoduleid=$newcmid->new_id;
} else {
if (!defined('RESTORE_SILENTLY')) {
echo "<p>Can't find new ID for cm $data->coursemoduleid, ignoring availability condition.</p>";
}
$status=false;
continue;
}
// Convert source cmid
if($data->sourcecmid) {
$newcmid=backup_getid($restore->backup_unique_code, 'course_modules', $data->sourcecmid);
if($newcmid) {
$data->sourcecmid=$newcmid->new_id;
} else {
if (!defined('RESTORE_SILENTLY')) {
echo "<p>Can't find new ID for source cm $data->sourcecmid, ignoring availability condition.</p>";
}
$status=false;
continue;
}
}
// Convert grade id
if($data->gradeitemid) {
$newgradeid=backup_getid($restore->backup_unique_code,
'grade_items',$data->gradeitemid);
if($newgradeid) {
$data->gradeitemid=$newgradeid->new_id;
} else {
if (!defined('RESTORE_SILENTLY')) {
echo "<p>Can't find new ID for grade item $data->gradeitemid, ignoring availability condition.</p>";
}
$status=false;
continue;
}
}
// Add record
if(!$DB->insert_record('course_modules_availability',$data)) {
if (!defined('RESTORE_SILENTLY')) {
echo "<p>Failed to insert availability data record.</p>";
}
$status=false;
continue;
}
}
}
} else {
$status = false;
}
@ -5618,6 +5677,12 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
isset($this->info->tempmod->completionview) ? $this->info->tempmod->completionview : 0;
$this->info->tempsection->mods[$this->info->tempmod->id]->completionexpected =
isset($this->info->tempmod->completionexpected) ? $this->info->tempmod->completionexpected : 0;
$this->info->tempsection->mods[$this->info->tempmod->id]->availablefrom =
isset($this->info->tempmod->availablefrom) ? $this->info->tempmod->availablefrom : 0;
$this->info->tempsection->mods[$this->info->tempmod->id]->availableuntil =
isset($this->info->tempmod->availableuntil) ? $this->info->tempmod->availableuntil : 0;
$this->info->tempsection->mods[$this->info->tempmod->id]->showavailability =
isset($this->info->tempmod->showavailability) ? $this->info->tempmod->showavailability : 0;
unset($this->info->tempmod);
}
@ -5669,6 +5734,15 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
case "COMPLETIONEXPECTED":
$this->info->tempmod->completionexpected = $this->getContents();
break;
case "AVAILABLEFROM":
$this->info->tempmod->availablefrom = $this->getContents();
break;
case "AVAILABLEUNTIL":
$this->info->tempmod->availableuntil = $this->getContents();
break;
case "SHOWAVAILABILITY":
$this->info->tempmod->showavailability = $this->getContents();
break;
default:
break;
}
@ -5794,6 +5868,40 @@ define('RESTORE_GROUPS_GROUPINGS', 3);
}
}
}
if (isset($this->tree[7]) && $this->tree[7] == "AVAILABILITYDATA") {
if($this->level == 8) {
switch($tagName) {
case 'AVAILABILITY':
// Got all data to make completion entry...
$this->info->tempavailability->coursemoduleid=$this->info->tempmod->id;
$this->info->availabilitydata[]=$this->info->tempavailability;
unset($this->info->tempavailability);
$this->info->tempavailability=new stdClass;
break;
}
}
if($this->level == 9) {
switch($tagName) {
case 'SOURCECMID' :
$this->info->tempavailability->sourcecmid=$this->getContents();
break;
case 'REQUIREDCOMPLETION' :
$this->info->tempavailability->requiredcompletion=$this->getContents();
break;
case 'GRADEITEMID' :
$this->info->tempavailability->gradeitemid=$this->getContents();
break;
case 'GRADEMIN' :
$this->info->tempavailability->grademin=$this->getContents();
break;
case 'GRADEMAX' :
$this->info->tempavailability->grademax=$this->getContents();
break;
}
}
}
}
//Stop parsing if todo = SECTIONS and tagName = SECTIONS (en of the tag, of course)

View File

@ -982,6 +982,9 @@ function get_array_of_activities($courseid) {
// groupmembersonly - is this instance visible to group members only
// extra - contains extra string to include in any link
global $CFG, $DB;
if(!empty($CFG->enableavailability)) {
require_once($CFG->libdir.'/conditionlib.php');
}
$mod = array();
@ -1005,7 +1008,17 @@ function get_array_of_activities($courseid) {
$mod[$seq]->groupmode = $rawmods[$seq]->groupmode;
$mod[$seq]->groupingid = $rawmods[$seq]->groupingid;
$mod[$seq]->groupmembersonly = $rawmods[$seq]->groupmembersonly;
$mod[$seq]->indent = $rawmods[$seq]->indent;
$mod[$seq]->completion = $rawmods[$seq]->completion;
$mod[$seq]->extra = "";
if(!empty($CFG->enableavailability)) {
condition_info::fill_availability_conditions($rawmods[$seq]);
$mod[$seq]->availablefrom = $rawmods[$seq]->availablefrom;
$mod[$seq]->availableuntil = $rawmods[$seq]->availableuntil;
$mod[$seq]->showavailability = $rawmods[$seq]->showavailability;
$mod[$seq]->conditionscompletion = $rawmods[$seq]->conditionscompletion;
$mod[$seq]->conditionsgrade = $rawmods[$seq]->conditionsgrade;
}
$modname = $mod[$seq]->mod;
$functionname = $modname."_get_coursemodule_info";
@ -1048,6 +1061,9 @@ function get_array_of_activities($courseid) {
*/
function &get_fast_modinfo(&$course, $userid=0) {
global $CFG, $USER, $DB;
if(!empty($CFG->enableavailability)) {
require_once($CFG->libdir.'/conditionlib.php');
}
static $cache = array();
@ -1132,9 +1148,27 @@ function &get_fast_modinfo(&$course, $userid=0) {
$cm->groupmode = $mod->groupmode;
$cm->groupingid = $mod->groupingid;
$cm->groupmembersonly = $mod->groupmembersonly;
$cm->indent = $mod->indent;
$cm->completion = $mod->completion;
$cm->extra = isset($mod->extra) ? urldecode($mod->extra) : '';
$cm->icon = isset($mod->icon) ? $mod->icon : '';
$cm->uservisible = true;
if(!empty($CFG->enableavailability)) {
// We must have completion information from modinfo. If it's not
// there, cache needs rebuilding
if(!isset($mod->availablefrom)) {
debugging('enableavailability option was changed; rebuilding '.
'cache for course '.$course->id);
rebuild_course_cache($course->id,true);
// Re-enter this routine to do it all properly
return get_fast_modinfo($course,$userid);
}
$cm->availablefrom = $mod->availablefrom;
$cm->availableuntil = $mod->availableuntil;
$cm->showavailability = $mod->showavailability;
$cm->conditionscompletion = $mod->conditionscompletion;
$cm->conditionsgrade = $mod->conditionsgrade;
}
// preload long names plurals and also check module is installed properly
if (!isset($modlurals[$cm->modname])) {
@ -1145,7 +1179,29 @@ function &get_fast_modinfo(&$course, $userid=0) {
}
$cm->modplural = $modlurals[$cm->modname];
if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $contexts[$cm->id], $userid)) {
if(!empty($CFG->enableavailability)) {
// Unfortunately the next call really wants to call
// get_fast_modinfo, but that would be recursive, so we fake up a
// modinfo for it already
if(empty($minimalmodinfo)) {
$minimalmodinfo=new stdClass();
$minimalmodinfo->cms=array();
foreach($info as $mod) {
$minimalcm=new stdClass();
$minimalcm->id=$mod->cm;
$minimalcm->name=urldecode($mod->name);
$minimalmodinfo->cms[$minimalcm->id]=$minimalcm;
}
}
// Get availability information
$ci = new condition_info($cm);
$cm->available=$ci->is_available($cm->availableinfo,true,$userid,
$minimalmodinfo);
} else {
$cm->available=true;
}
if ((!$cm->visible or !$cm->available) and !has_capability('moodle/course:viewhiddenactivities', $contexts[$cm->id], $userid)) {
$cm->uservisible = false;
} else if (!empty($CFG->enablegroupings) and !empty($cm->groupmembersonly)
@ -1183,7 +1239,7 @@ function &get_fast_modinfo(&$course, $userid=0) {
* Returns a number of useful structures for course displays
*/
function get_all_mods($courseid, &$mods, &$modnames, &$modnamesplural, &$modnamesused) {
global $DB;
global $DB,$COURSE;
$mods = array(); // course modules indexed by id
$modnames = array(); // all course module names (except resource!)
@ -1202,7 +1258,10 @@ function get_all_mods($courseid, &$mods, &$modnames, &$modnamesplural, &$modname
print_error("nomodules", 'debug');
}
if ($rawmods = get_course_mods($courseid)) {
$course = ($courseid==$COURSE->id) ? $COURSE : $DB->get_record('course',array('id'=>$courseid));
$modinfo = get_fast_modinfo($course);
if ($rawmods=$modinfo->cms) {
foreach($rawmods as $mod) { // Index the mods
if (empty($modnames[$mod->modname])) {
continue;
@ -1336,7 +1395,8 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
}
if (isset($modinfo->cms[$modnumber])) {
if (!$modinfo->cms[$modnumber]->uservisible) {
if (!$modinfo->cms[$modnumber]->uservisible &&
empty($modinfo->cms[$modnumber]->showavailability)) {
// visibility shortcut
continue;
}
@ -1345,7 +1405,8 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
// module not installed
continue;
}
if (!coursemodule_visible_for_user($mod)) {
if (!coursemodule_visible_for_user($mod) &&
empty($mod->showavailability)) {
// full visibility check
continue;
}
@ -1366,11 +1427,11 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
$extra = '';
if (!empty($modinfo->cms[$modnumber]->extra)) {
$extra = $modinfo->cms[$modnumber]->extra;
$extra = $modinfo->cms[$modnumber]->extra;
}
if ($mod->modname == "label") {
if (!$mod->visible) {
if (!$mod->visible || !$mod->uservisible) {
echo "<div class=\"dimmed_text\">";
}
echo format_text($extra, FORMAT_HTML, $labelformatoptions);
@ -1416,17 +1477,27 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
$altname = get_accesshide(' '.$altname);
}
$linkcss = $mod->visible ? "" : " class=\"dimmed\" ";
echo '<a '.$linkcss.' '.$extra. // Title unnecessary!
' href="'.$CFG->wwwroot.'/mod/'.$mod->modname.'/view.php?id='.$mod->id.'">'.
'<img src="'.$icon.'" class="activityicon" alt="" /> <span>'.
$instancename.$altname.'</span></a>';
// We may be displaying this just in order to show information
// about visibility, without the actual link
if($mod->uservisible) {
// Display normal module link
$linkcss = $mod->visible ? "" : " class=\"dimmed\" ";
echo '<a '.$linkcss.' '.$extra. // Title unnecessary!
' href="'.$CFG->wwwroot.'/mod/'.$mod->modname.'/view.php?id='.$mod->id.'">'.
'<img src="'.$icon.'" class="activityicon" alt="" /> <span>'.
$instancename.$altname.'</span></a>';
if (!empty($CFG->enablegroupings) && !empty($mod->groupingid) && has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
if (!isset($groupings)) {
$groupings = groups_get_all_groupings($course->id);
if (!empty($CFG->enablegroupings) && !empty($mod->groupingid) && has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
if (!isset($groupings)) {
$groupings = groups_get_all_groupings($course->id);
}
echo " <span class=\"groupinglabel\">(".format_string($groupings[$mod->groupingid]->name).')</span>';
}
echo " <span class=\"groupinglabel\">(".format_string($groupings[$mod->groupingid]->name).')</span>';
} else {
// Display greyed-out text of link
echo '<span class="dimmed_text" '.$extra.' >'.
'<img src="'.$icon.'" class="activityicon" alt="" /> <span>'.
$instancename.$altname.'</span></span>';
}
}
if ($usetracking && $mod->modname == 'forum') {
@ -1460,7 +1531,8 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
$completion=$hidecompletion
? COMPLETION_TRACKING_NONE
: $completioninfo->is_enabled($mod);
if($completion!=COMPLETION_TRACKING_NONE && isloggedin() && !isguestuser()) {
if($completion!=COMPLETION_TRACKING_NONE && isloggedin() &&
!isguestuser() && $mod->uservisible) {
$completiondata=$completioninfo->get_data($mod,true);
$completionicon='';
if($isediting) {
@ -1524,6 +1596,21 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
}
}
// Show availability information (for someone who isn't allowed to
// see the activity itself, or for staff)
if(!$mod->uservisible) {
echo '<div class="availabilityinfo">'.$mod->availableinfo.'</div>';
} else if($isediting && !empty($CFG->enableavailability)) {
$ci = new condition_info($mod);
$fullinfo=$ci->get_full_information();
if($fullinfo) {
echo '<div class="availabilityinfo">'.get_string($mod->showavailability
? 'userrestriction_visible'
: 'userrestriction_hidden','condition',
$fullinfo).'</div>';
}
}
echo "</li>\n";
}

View File

@ -172,6 +172,8 @@
print_error('cannotupdatelevel');
}
rebuild_course_cache($cm->course);
if (SITEID == $cm->course) {
redirect($CFG->wwwroot);
} else {

View File

@ -6,6 +6,7 @@
require_once("lib.php");
require_once($CFG->libdir.'/gradelib.php');
require_once($CFG->libdir.'/completionlib.php');
require_once($CFG->libdir.'/conditionlib.php');
$add = optional_param('add', 0, PARAM_ALPHA);
$update = optional_param('update', 0, PARAM_INT);
@ -123,6 +124,11 @@
$form->completionview = $cm->completionview;
$form->completionexpected = $cm->completionexpected;
$form->completionusegrade = is_null($cm->completiongradeitemnumber) ? 0 : 1;
if(!empty($CFG->enableavailability)) {
$form->availablefrom = $cm->availablefrom;
$form->availableuntil = $cm->availableuntil;
$form->showavailability = $cm->showavailability;
}
if ($items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$form->modulename,
'iteminstance'=>$form->instance, 'courseid'=>$course->id))) {
@ -285,6 +291,12 @@
$cm->completionview = $fromform->completionview;
$cm->completionexpected = $fromform->completionexpected;
}
if(!empty($CFG->enableavailability)) {
$cm->availablefrom = $fromform->availablefrom;
$cm->availableuntil = $fromform->availableuntil;
$cm->showavailability = $fromform->showavailability;
condition_info::update_cm_from_form($cm,$fromform,true);
}
if (!$DB->update_record('course_modules', $cm)) {
print_error('cannotupdatecoursemodule');
@ -335,6 +347,11 @@
$newcm->completionview = $fromform->completionview;
$newcm->completionexpected = $fromform->completionexpected;
}
if(!empty($CFG->enableavailability)) {
$newcm->availablefrom = $fromform->availablefrom;
$newcm->availableuntil = $fromform->availableuntil;
$newcm->showavailability = $fromform->showavailability;
}
if (!$fromform->coursemodule = add_course_module($newcm)) {
print_error('cannotaddcoursemodule');
@ -381,6 +398,12 @@
set_coursemodule_idnumber($fromform->coursemodule, $fromform->cmidnumber);
}
// Set up conditions
if($CFG->enableavailability) {
condition_info::update_cm_from_form(
(object)array('id'=>$fromform->coursemodule),$fromform,false);
}
add_to_log($course->id, "course", "add mod",
"../mod/$fromform->modulename/view.php?id=$fromform->coursemodule",
"$fromform->modulename $fromform->instance");

View File

@ -1,6 +1,11 @@
<?php //$Id$
require_once ($CFG->libdir.'/formslib.php');
require_once($CFG->libdir.'/completionlib.php');
if(!empty($CFG->enablecompletion)) {
require_once($CFG->libdir.'/completionlib.php');
}
if(!empty($CFG->enableavailability)) {
require_once($CFG->libdir.'/conditionlib.php');
}
/**
* This class adds extra methods to form wrapper specific to be used for module
@ -174,6 +179,33 @@ class moodleform_mod extends moodleform {
$mform->freeze($this->_customcompletionelements);
}
}
// Availability conditions
if (!empty($CFG->enableavailability) && $this->_cm) {
$ci = new condition_info($this->_cm);
$fullcm=$ci->get_full_course_module();
$num=0;
foreach($fullcm->conditionsgrade as $gradeitemid=>$minmax) {
$groupelements=$mform->getElement('conditiongradegroup['.$num.']')->getElements();
$groupelements[0]->setValue($gradeitemid);
// These numbers are always in the format 0.00000 - the rtrims remove any final zeros and,
// if it is a whole number, the decimal place.
$groupelements[2]->setValue(is_null($minmax->min)?'':rtrim(rtrim($minmax->min,'0'),'.'));
$groupelements[4]->setValue(is_null($minmax->max)?'':rtrim(rtrim($minmax->max,'0'),'.'));
$num++;
}
if ($completion->is_enabled()) {
$num=0;
foreach($fullcm->conditionscompletion as $othercmid=>$state) {
$groupelements=$mform->getElement('conditioncompletiongroup['.$num.']')->getElements();
$groupelements[0]->setValue($othercmid);
$groupelements[1]->setValue($state);
$num++;
}
}
}
}
// form verification
@ -230,8 +262,9 @@ class moodleform_mod extends moodleform {
if (is_object($default_values)) {
$default_values = (array)$default_values;
}
$this->data_preprocessing($default_values);
parent::set_data($default_values); //never slashed for moodleform_mod
$this->data_preprocessing($default_values);
parent::set_data($default_values);
}
/**
@ -352,8 +385,90 @@ class moodleform_mod extends moodleform {
$mform->addElement('select', 'gradecat', get_string('gradecategory', 'grades'), $categories);
}
if (!empty($CFG->enableavailability)) {
// Conditional availability
$mform->addElement('header', '', get_string('availabilityconditions', 'condition'));
$mform->addElement('date_selector', 'availablefrom', get_string('availablefrom', 'condition'), array('optional'=>true));
$mform->setHelpButton('availablefrom', array('conditiondates', get_string('help_conditiondates', 'condition'), 'condition'));
$mform->addElement('date_selector', 'availableuntil', get_string('availableuntil', 'condition'), array('optional'=>true));
$mform->setHelpButton('availableuntil', array('conditiondates', get_string('help_conditiondates', 'condition'), 'condition'));
// Conditions based on grades
$gradeoptions=array();
$items=grade_item::fetch_all(array('courseid'=>$COURSE->id));
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));
$mform->setType('conditiongrademin',PARAM_FLOAT);
$mform->setType('conditiongrademax',PARAM_FLOAT);
$group = $mform->createElement('group','conditiongradegroup',
get_string('gradecondition', 'condition'),$grouparray);
// Get version with condition info and store it so we don't ask
// twice
if(!empty($this->_cm)) {
$ci = new condition_info($this->_cm,CONDITION_MISSING_EXTRATABLE);
$this->_cm=$ci->get_full_course_module();
$count=count($this->_cm->conditionsgrade)+1;
} else {
$count=1;
}
$this->repeat_elements(array($group),$count,array(),'conditiongraderepeats','conditiongradeadds',2,
get_string('addgrades','condition'),true);
$mform->setHelpButton('conditiongradegroup[0]', array('gradecondition', get_string('help_gradecondition', 'condition'), '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) {
$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=empty($this->_cm) ? 1 : count($this->_cm->conditionscompletion)+1;
$this->repeat_elements(array($group),$count,array(),
'conditioncompletionrepeats','conditioncompletionadds',2,
get_string('addcompletions','condition'),true);
$mform->setHelpButton('conditioncompletiongroup[0]', array('completioncondition', get_string('help_completioncondition', 'condition'), 'condition'));
}
// Do we display availability info to students?
$mform->addElement('select', 'showavailability', get_string('showavailability', 'condition'),
array(CONDITION_STUDENTVIEW_SHOW=>get_string('showavailability_show', 'condition'),
CONDITION_STUDENTVIEW_HIDE=>get_string('showavailability_hide', 'condition')));
$mform->setDefault('showavailability', CONDITION_STUDENTVIEW_SHOW);
$mform->setHelpButton('showavailability', array('showavailability', get_string('help_showavailability', 'condition'), 'condition'));
}
// Conditional activities: completion tracking section
$completion = new completion_info($COURSE);
if(!isset($completion)) {
$completion = new completion_info($COURSE);
}
if ($completion->is_enabled()) {
$mform->addElement('header', '', get_string('activitycompletion', 'completion'));
@ -362,7 +477,7 @@ class moodleform_mod extends moodleform {
$mform->addElement('submit', 'unlockcompletion', get_string('unlockcompletion', 'completion'));
$mform->registerNoSubmitButton('unlockcompletion');
$mform->addElement('hidden', 'completionunlocked', 0);
$mform->addElement('select', 'completion', get_string('completion', 'completion'),
array(COMPLETION_TRACKING_NONE=>get_string('completion_none', 'completion'),
COMPLETION_TRACKING_MANUAL=>get_string('completion_manual', 'completion')));

View File

@ -0,0 +1,35 @@
<?php
$string['addgrades']='Add {no} grade conditions to form';
$string['addcompletions']='Add {no} activity conditions to form';
$string['availabilityconditions']='Restrict availability';
$string['availablefrom']='Only available from';
$string['availableuntil']='Only available until';
$string['completion_complete']=' must be marked complete';
$string['completion_incomplete']=' must not be marked complete';
$string['completion_pass']=' must be complete with pass grade';
$string['completion_fail']=' must be complete with fail grade';
$string['configenableavailability']='When enabled, this lets you set conditions (based on date, grade, or completion) that control whether an activity is available.';
$string['enableavailability']='Enable conditional availability';
$string['grade_atleast']='must be &ge;';
$string['grade_upto']='and &lt;';
$string['gradecondition']='Grade condition';
$string['completioncondition']='Activity completion condition';
$string['help_conditiondates']='available dates';
$string['help_showavailability']='display of unavailable activities';
$string['none']='(none)';
$string['requires_completion_0']='Not available unless the activity <strong>$a</strong> is incomplete.';
$string['requires_completion_1']='Not available until the activity <strong>$a</strong> is marked complete.';
$string['requires_completion_2']='Not available until the activity <strong>$a</strong> is complete and passed.';
$string['requires_completion_3']='Not available unless the activity <strong>$a</strong> is complete and failed.';
$string['requires_date']='Not available until $a.';
$string['requires_date_before']='Not available from $a.';
$string['requires_grade_any']='Not available until you have a grade in <strong>$a</strong>.';
$string['requires_grade_min']='Not available until you achieve a required score in <strong>$a</strong>.';
$string['requires_grade_max']='Not available unless you get an appropriate score in <strong>$a</strong>.';
$string['requires_grade_range']='Not available unless you get a particular score in <strong>$a</strong>.';
$string['showavailability']='Before activity is available';
$string['showavailability_show']='Show activity greyed-out, with restriction information';
$string['showavailability_hide']='Hide activity entirely';
$string['userrestriction_visible']='Activity conditionally restricted: &lsquo;$a&rsquo;';
$string['userrestriction_hidden']='Activity conditionally restricted (completely hidden, no message): &lsquo;$a&rsquo;';
?>

View File

@ -0,0 +1,20 @@
<h1>Activity completion condition</h1>
<p>
You can set a condition based on whether the user has completed another
activity.
</p>
<p>
This feature uses the completion options that have been configured for the
other activity. You can choose whether the activity must be complete,
incomplete, complete and passed, or complete and failed. The final two
options only work if you use grade-based completion and set a pass mark on
the grade item. (Please look at the documentation for activity completion
if this is unclear.)
</p>
<p>
You can add more than one completion condition. All conditions must be met in
order for the activity to appear.
</p>

View File

@ -0,0 +1,23 @@
<h1>Available dates</h1>
<p>
Using the 'Only available from' and 'Only available until' dates, you can make
an activity appear or disappear. The activity is only shown to students from the
'available from' date, and it disappears on the 'available until' date. Students
cannot access it outside those times, even if they guess the URL.
</p>
<p>
By default both dates are disabled, meaning that the activity is available at
any time (as long as the student can access the course).
</p>
<ul>
<li> If you choose to show information about an activity that is unavailable,
then before the 'available from' date, students will see the activity
greyed-out, with informational text about the date that it appears.</li>
<li> The activity completely vanishes on the 'available to' date, even if
you've chosen to show information.</li>
<li> Setting 'Visible' to 'Hide' overrides these settings. If you set 'Visible'
to 'Hide', the activity is never available regardless of date.</li>
</ul>

View File

@ -0,0 +1,31 @@
<h1>Grade condition</h1>
<p>
You can specify a condition on any grade in the course: the full course grade,
the grade for any activity, or a custom grade that you create manually.
</p>
<p>
You can enter either a minimum value (&ge;), a maximum value (&lt;), both, or
neither. The activity will only appear if the student has a value for the
specified grade, and if it falls within any specified number range.
</p>
<p>
You can add more than one grade condition. All conditions must be met in order
for the activity to appear.
</p>
<ul>
<li>The range numbers can be fractional (with up to five decimal places) if
necessary. </li>
<li>Be careful with the maximum value; if the maximum is 7, a student who
scores exactly 7 will not see the activity. You could set it to 7.01 if
you really wanted to include 7.</li>
<li>If creating several different activities that appear according to grade
ranges, use the same number for the maximum of one activity, and the minimum
of the next. For example, you might create one activity with a maximum of 7
and another with a minimum of 7. The first would appear to everyone scoring
between 0 and 6.99999, and the second would appear to everyone scoring 7.00000
to 10. This guarantees that everyone with a grade will see one or other.</li>
</ul>

View File

@ -0,0 +1,27 @@
<h1>Unavailable activity display</h1>
<p>
When an activity is unavailable due to the restrictions in this box, there are
two possibilities:
</p>
<ol>
<li> The activity displays to users, but as greyed-out text instead of a link.
Informational text below the activity indicates when, or under what
conditions, it will become available.</li>
<li> The activity does not display to users at all.</li>
</ol>
<p>
In both cases, once the activity becomes available, it displays as normal.
</p>
<ul>
<li>Users with the 'view hidden activities' capability can still see
unavailable activities, regardless of this setting. The informational text
always appears to them.</li>
<li>This option does not affect the standard 'visibility' option (also controlled
by the eye icon). You can still use this icon to quickly and completely hide
any activity from students, for example if you find a problem with the
activity.</li>
</ul>

View File

@ -394,13 +394,15 @@ class completion_info {
* Obtains completion data for a particular activity and user (from the
* session cache if available, or by SQL query)
*
* @param object $cm Activity
* @param object $cm Activity; only required field is ->id
* @param bool $wholecourse If true (default false) then, when necessary to
* fill the cache, retrieves information from the entire course not just for
* this one activity
* @param int $userid User ID or 0 (default) for current user
* @param array $modinfo For unit testing only, supply the value
* here. Otherwise the method calls get_fast_modinfo
* @param array $modinfo Supply the value here - this is used for unit
* testing and so that it can be called recursively from within
* get_fast_modinfo. (Needs only list of all CMs with IDs.)
* Otherwise the method calls get_fast_modinfo itself.
* @return object Completion data (record from course_modules_completion)
* @throws Exception In some cases where the requested course-module is not
* found on the specified course
@ -632,7 +634,7 @@ class completion_info {
foreach ($users as $user) {
$userids[] = $user->id;
$resultobject->users[$user->id]=$user;
$resultobject->users[$user->id]->progress=array();
$resultobject->users[$user->id]->progress=array();
}
for($i=0; $i<count($userids); $i+=1000) {
@ -716,22 +718,24 @@ WHERE
}
/**
* This temporary function is intended to be replaced once a Moodle exception
* system is agreed. Code that used to call this function should instead
* throw an exception, so this function should be deleted. The function is
* only used internally.
*
* This is to be used only for system errors (things that shouldn't happen)
* and not user-level errors.
*
* @param string $error Error string (will not be displayed to user unless
* debugging is enabled)
* @throws moodle_exception Exception with the error string as debug info
*/
function internal_systemerror($error) {
global $CFG;
throw new moodle_exception('err_system','completion',
$CFG->wwwroot.'/course/view.php?id='.$this->course->id,null,$error);
}
debugging($error, DEBUG_ALL);
print_error('err_system', 'completion', $CFG->wwwroot.'/course/view.php?id='.$this->course->id);
/** For testing only. Wipes information cached in user session. */
static function wipe_session_cache() {
global $SESSION;
unset($SESSION->completioncache);
unset($SESSION->completioncacheuserid);
}
}

538
lib/conditionlib.php Normal file
View File

@ -0,0 +1,538 @@
<?php
// Used for tracking conditions that apply before activities are displayed
// to students ('conditional availability').
/** The activity is not displayed to students at all when conditions aren't met. */
define('CONDITION_STUDENTVIEW_HIDE',0);
/** The activity is displayed to students as a greyed-out name, with informational
text that explains the conditions under which it will be available. */
define('CONDITION_STUDENTVIEW_SHOW',1);
/** The $cm variable is expected to contain all completion-related data */
define('CONDITION_MISSING_NOTHING',0);
/** The $cm variable is expected to contain the fields from course_modules but
not the course_modules_availability data */
define('CONDITION_MISSING_EXTRATABLE',1);
/** The $cm variable is expected to contain nothing except the ID */
define('CONDITION_MISSING_EVERYTHING',2);
class condition_info {
private $cm,$gotdata;
/**
* Constructs with course-module details.
*
* @param object $cm Moodle course-module object. May have extra fields
* ->conditionsgrade, ->conditionscompletion which should come from
* get_fast_modinfo. Should have ->availablefrom, ->availableuntil,
* and ->showavailability, ->course; but the only required thing is ->id.
* @param int $expectingmissing Used to control whether or not a developer
* debugging message (performance warning) will be displayed if some of
* the above data is missing and needs to be retrieved; a
* CONDITION_MISSING_xx constant
* @param bool $loaddata If you need a 'write-only' object, set this value
* to false to prevent database access from constructor
* @return condition_info Object which can retrieve information about the
* activity
*/
public function __construct($cm,$expectingmissing=CONDITION_MISSING_NOTHING,
$loaddata=true) {
global $DB;
// Check ID as otherwise we can't do the other queries
if(empty($cm->id)) {
throw new coding_exception("Invalid parameters; course-module ID not included");
}
// If not loading data, don't do anything else
if(!$loaddata) {
$this->cm=(object)array('id'=>$cm->id);
$this->gotdata=false;
return;
}
// Missing basic data from course_modules
if(!isset($cm->availablefrom) || !isset($cm->availableuntil) ||
!isset($cm->showavailability) || !isset($cm->course)) {
if($expectingmissing<CONDITION_MISSING_EVERYTHING) {
debugging('Performance warning: condition_info constructor is
faster if you pass in $cm with at least basic fields
(availablefrom,availableuntil,showavailability,course).
[This warning can be disabled, see phpdoc.]',
DEBUG_DEVELOPER);
}
$cm=$DB->get_record('course_modules',array('id'=>$cm->id),
'id,course,availablefrom,availableuntil,showavailability');
}
$this->cm=clone($cm);
$this->gotdata=true;
// Missing extra data
if(!isset($cm->conditionsgrade) || !isset($cm->conditionscompletion)) {
if($expectingmissing<CONDITION_MISSING_EXTRATABLE) {
debugging('Performance warning: condition_info constructor is
faster if you pass in a $cm from get_fast_modinfo.
[This warning can be disabled, see phpdoc.]',
DEBUG_DEVELOPER);
}
self::fill_availability_conditions($this->cm);
}
}
/**
* Adds the extra availability conditions (if any) into the given
* course-module object.
*
* @param object &$cm Moodle course-module data object
*/
public static function fill_availability_conditions(&$cm) {
if(empty($cm->id)) {
throw new coding_exception("Invalid parameters; course-module ID not included");
}
// Does nothing if the variables are already present
if(!isset($cm->conditionsgrade) ||
!isset($cm->conditionscompletion)) {
$cm->conditionsgrade=array();
$cm->conditionscompletion=array();
global $DB,$CFG;
$conditions=$DB->get_records_sql($sql="
SELECT
cma.id as cmaid, gi.*,cma.sourcecmid,cma.requiredcompletion,cma.gradeitemid,
cma.grademin as conditiongrademin, cma.grademax as conditiongrademax
FROM
{course_modules_availability} cma
LEFT JOIN {grade_items} gi ON gi.id=cma.gradeitemid
WHERE
coursemoduleid=?",array($cm->id));
foreach($conditions as $condition) {
if(!is_null($condition->sourcecmid)) {
$cm->conditionscompletion[$condition->sourcecmid]=
$condition->requiredcompletion;
} else {
$minmax=new stdClass;
$minmax->min=$condition->conditiongrademin;
$minmax->max=$condition->conditiongrademax;
$minmax->name=self::get_grade_name($condition);
$cm->conditionsgrade[$condition->gradeitemid]=$minmax;
}
}
}
}
/**
* Obtains the name of a grade item.
* @param object $gradeitemobj Object from get_record on grade_items table,
* (can be empty if you want to just get !missing)
* @return string Name of item of !missing if it didn't exist
*/
private static function get_grade_name($gradeitemobj) {
global $CFG;
if(isset($gradeitemobj->id)) {
require_once($CFG->libdir.'/gradelib.php');
$item=new grade_item;
grade_object::set_properties($item,$gradeitemobj);
return $item->get_name();
} else {
return '!missing'; // Ooops, missing grade
}
}
/**
* @return A course-module object with all the information required to
* determine availability.
* @throws coding_exception If data wasn't loaded
*/
public function get_full_course_module() {
$this->require_data();
return $this->cm;
}
/**
* Adds to the database a condition based on completion of another module.
* @param int $cmid ID of other module
* @param int $requiredcompletion COMPLETION_xx constant
*/
public function add_completion_condition($cmid,$requiredcompletion) {
// Add to DB
global $DB;
$DB->insert_record('course_modules_availability',
(object)array('coursemoduleid'=>$this->cm->id,
'sourcecmid'=>$cmid,'requiredcompletion'=>$requiredcompletion),
false);
// Store in memory too
$this->cm->conditionscompletion[$cmid]=$requiredcompletion;
}
/**
* Adds to the database a condition based on the value of a grade item.
* @param int $gradeitemid ID of grade item
* @param float $min Minimum grade (>=), up to 5 decimal points, or null if none
* @param float $max Maximum grade (<), up to 5 decimal points, or null if none
* @param bool $updateinmemory If true, updates data in memory; otherwise,
* memory version may be out of date (this has performance consequences,
* so don't do it unless it really needs updating)
*/
public function add_grade_condition($gradeitemid,$min,$max,$updateinmemory=false) {
// Normalise nulls
if($min==='') {
$min=null;
}
if($max==='') {
$max=null;
}
// Add to DB
global $DB;
$DB->insert_record('course_modules_availability',
(object)array('coursemoduleid'=>$this->cm->id,
'gradeitemid'=>$gradeitemid,'grademin'=>$min,'grademax'=>$max),
false);
// Store in memory too
if($updateinmemory) {
$this->cm->conditionsgrade[$gradeitemid]=(object)array(
'min'=>$min,'max'=>$max);
$this->cm->conditionsgrade[$gradeitemid]->name=
self::get_grade_name($DB->get_record('grade_items',
array('id'=>$gradeitemid)));
}
}
/**
* Erases from the database all conditions for this activity.
*/
public function wipe_conditions() {
// Wipe from DB
global $DB;
$DB->delete_records('course_modules_availability',
array('coursemoduleid'=>$this->cm->id));
// And from memory
$this->cm->conditionsgrade=array();
$this->cm->conditionscompletion=array();
}
/**
* Obtains a string describing all availability restrictions (even if
* they do not apply any more).
* @param object $modinfo Usually leave as null for default. Specify when
* calling recursively from inside get_fast_modinfo. The value supplied
* here must include list of all CMs with 'id' and 'name'
* @return string Information string (for admin) about all restrictions on
* this item
* @throws coding_exception If data wasn't loaded
*/
public function get_full_information($modinfo=null) {
$this->require_data();
global $COURSE,$DB;
$information='';
// Completion conditions
if(count($this->cm->conditionscompletion)>0) {
if($this->cm->course==$COURSE->id) {
$course=$COURSE;
} else {
$course=$DB->get_record('course',array('id'=>$this->cm->course),'id,enablecompletion,modinfo');
}
foreach($this->cm->conditionscompletion as $cmid=>$expectedcompletion) {
if(!$modinfo) {
$modinfo=get_fast_modinfo($course);
}
$information.=get_string(
'requires_completion_'.$expectedcompletion,
'condition',$modinfo->cms[$cmid]->name).' ';
}
}
// Grade conditions
if(count($this->cm->conditionsgrade)>0) {
foreach($this->cm->conditionsgrade as $gradeitemid=>$minmax) {
// String depends on type of requirement. We are coy about
// the actual numbers, in case grades aren't released to
// students.
if(is_null($minmax->min) && is_null($minmax->max)) {
$string='any';
} else if(is_null($minmax->max)) {
$string='min';
} else if(is_null($minmax->min)) {
$string='max';
} else {
$string='range';
}
$information.=get_string('requires_grade_'.$string,'condition',$minmax->name).' ';
}
}
// Dates
if($this->cm->availablefrom) {
$information.=get_string('requires_date','condition',userdate(
$this->cm->availablefrom,get_string('strftimedate','langconfig')));
}
if($this->cm->availableuntil) {
$information.=get_string('requires_date_before','condition',userdate(
$this->cm->availableuntil,get_string('strftimedate','langconfig')));
}
$information=trim($information);
return $information;
}
/**
* Determines whether this particular course-module is currently available
* according to these criteria.
*
* - This does not include the 'visible' setting (i.e. this might return
* true even if visible is false); visible is handled independently.
* - This does not take account of the viewhiddenactivities capability.
* That should apply later.
*
* @param string &$information If the item has availability restrictions,
* a string that describes the conditions will be stored in this variable;
* if this variable is set blank, that means don't display anything
* @param bool $grabthelot Performance hint: if true, caches information
* required for all course-modules, to make the front page and similar
* pages work more quickly (works only for current user)
* @param int $userid If set, specifies a different user ID to check availability for
* @param object $modinfo Usually leave as null for default. Specify when
* calling recursively from inside get_fast_modinfo. The value supplied
* here must include list of all CMs with 'id' and 'name'
* @return bool True if this item is available to the user, false otherwise
* @throws coding_exception If data wasn't loaded
*/
public function is_available(&$information,$grabthelot=false,$userid=0,$modinfo=null) {
$this->require_data();
global $COURSE,$DB;
$available=true;
$information='';
// Check each completion condition
if(count($this->cm->conditionscompletion)>0) {
if($this->cm->course==$COURSE->id) {
$course=$COURSE;
} else {
$course=$DB->get_record('course',array('id'=>$this->cm->course),'id,enablecompletion,modinfo');
}
$completion=new completion_info($course);
foreach($this->cm->conditionscompletion as $cmid=>$expectedcompletion) {
// The completion system caches its own data
$completiondata=$completion->get_data((object)array('id'=>$cmid),
$grabthelot,$userid,$modinfo);
$thisisok=true;
if($expectedcompletion==COMPLETION_COMPLETE) {
// 'Complete' also allows the pass, fail states
switch($completiondata->completionstate) {
case COMPLETION_COMPLETE:
case COMPLETION_COMPLETE_FAIL:
case COMPLETION_COMPLETE_PASS:
break;
default:
$thisisok=false;
}
} else {
// Other values require exact match
if($completiondata->completionstate!=$expectedcompletion) {
$thisisok=false;
}
}
if(!$thisisok) {
$available=false;
if(!$modinfo) {
$modinfo=get_fast_modinfo($course);
}
$information.=get_string(
'requires_completion_'.$expectedcompletion,
'condition',$modinfo->cms[$cmid]->name).' ';
}
}
}
// Check each grade condition
if(count($this->cm->conditionsgrade)>0) {
foreach($this->cm->conditionsgrade as $gradeitemid=>$minmax) {
$score=$this->get_cached_grade_score($gradeitemid,$grabthelot,$userid);
if($score===false ||
(!is_null($minmax->min) && $score<$minmax->min) ||
(!is_null($minmax->max) && $score>=$minmax->max)) {
// Grade fail
$available=false;
// String depends on type of requirement. We are coy about
// the actual numbers, in case grades aren't released to
// students.
if(is_null($minmax->min) && is_null($minmax->max)) {
$string='any';
} else if(is_null($minmax->max)) {
$string='min';
} else if(is_null($minmax->min)) {
$string='max';
} else {
$string='range';
}
$information.=get_string('requires_grade_'.$string,'condition',$minmax->name).' ';
}
}
}
// Test dates
if($this->cm->availablefrom) {
if(time() < $this->cm->availablefrom) {
$available=false;
$information.=get_string('requires_date','condition',userdate(
$this->cm->availablefrom,get_string('strftimedate','langconfig')));
}
}
if($this->cm->availableuntil) {
if(time() >= $this->cm->availableuntil) {
$available=false;
// But we don't display any information about this case. This is
// because the only reason to set a 'disappear' date is usually
// to get rid of outdated information/clutter in which case there
// is no point in showing it...
// Note it would be nice if we could make it so that the 'until'
// date appears below the item while the item is still accessible,
// unfortunately this is not possible in the current system. Maybe
// later, or if somebody else wants to add it.
}
}
$information=trim($information);
return $available;
}
/**
* @return bool True if information about availability should be shown to
* normal users
* @throws coding_exception If data wasn't loaded
*/
public function show_availability() {
$this->require_data();
return $this->cm->showavailability;
}
/**
* Internal function cheks that data was loaded.
* @throws coding_exception If data wasn't loaded
*/
private function require_data() {
if(!$this->gotdata) {
throw new coding_exception('Error: cannot call when info was '.
'constructed without data');
}
}
/**
* Obtains a grade score. Note that this score should not be displayed to
* the user, because gradebook rules might prohibit that. It may be a
* non-final score subject to adjustment later.
*
* @param int $gradeitemid Grade item ID we're interested in
* @param bool $grabthelot If true, grabs all scores for current user on
* this course, so that later ones come from cache
* @param int $userid Set if requesting grade for a different user (does
* not use cache)
* @return float Grade score, or false if user does not have a grade yet
*/
private function get_cached_grade_score($gradeitemid,$grabthelot=false,$userid=0) {
global $USER, $DB, $SESSION;
if($userid==0 || $userid=$USER->id) {
// For current user, go via cache in session
if(empty($SESSION->gradescorecache) || $SESSION->gradescorecacheuserid!=$USER->id) {
$SESSION->gradescorecache=array();
$SESSION->gradescorecacheuserid=$USER->id;
}
if(!array_key_exists($gradeitemid,$SESSION->gradescorecache)) {
if($grabthelot) {
// Get all grades for the current course
$rs=$DB->get_recordset_sql("
SELECT
gi.id,gg.finalgrade
FROM
{grade_items} gi
LEFT JOIN {grade_grades} gg ON gi.id=gg.itemid AND gg.userid=?
WHERE
gi.courseid=?",array($USER->id,$this->cm->course));
foreach($rs as $record) {
$SESSION->gradescorecache[$record->id]=
is_null($record->finalgrade)
? false
: $record->finalgrade;
}
$rs->close();
// And if it's still not set, well it doesn't exist (eg
// maybe the user set it as a condition, then deleted the
// grade item) so we call it false
if(!array_key_exists($gradeitemid,$SESSION->gradescorecache)) {
$SESSION->gradescorecache[$gradeitemid]=false;
}
} else {
// Just get current grade
$score=$DB->get_field('grade_grades','finalgrade',array(
'userid'=>$USER->id,'itemid'=>$gradeitemid));
// Treat the case where row exists but is null, same as
// case where row doesn't exist
if(is_null($score)) {
$score=false;
}
$SESSION->gradescorecache[$gradeitemid]=$score;
}
}
return $SESSION->gradescorecache[$gradeitemid];
} else {
// Not the current user, so request the score individually
$score=$DB->get_field('grade_grades','finalgrade',array(
'userid'=>$userid,'itemid'=>$gradeitemid));
if($score===null) {
$score=false;
}
return $score;
}
}
/** For testing only. Wipes information cached in user session. */
static function wipe_session_cache() {
global $SESSION;
unset($SESSION->gradescorecache);
unset($SESSION->gradescorecacheuserid);
}
/**
* Utility function called by modedit.php; updates the
* course_modules_availability table based on the module form data.
*
* @param object $cm Course-module with as much data as necessary, min id
* @param unknown_type $fromform
* @param unknown_type $wipefirst
*/
public static function update_cm_from_form($cm,$fromform,$wipefirst=true) {
$ci=new condition_info($cm,CONDITION_MISSING_EVERYTHING,false);
if($wipefirst) {
$ci->wipe_conditions();
}
foreach($fromform->conditiongradegroup as $record) {
if($record['conditiongradeitemid']) {
$ci->add_grade_condition($record['conditiongradeitemid'],
$record['conditiongrademin'],$record['conditiongrademax']);
}
}
if(isset($fromform->conditioncompletiongroup)) {
foreach($fromform->conditioncompletiongroup as $record) {
if($record['conditionsourcecmid']) {
$ci->add_completion_condition($record['conditionsourcecmid'],
$record['conditionrequiredcompletion']);
}
}
}
}
}
?>

View File

@ -1835,7 +1835,7 @@ function instance_is_visible($moduletype, $module) {
* @return bool
*/
function coursemodule_visible_for_user($cm, $userid=0) {
global $USER;
global $USER,$CFG;
if (empty($cm->id)) {
debugging("Incorrect course module parameter!", DEBUG_DEVELOPER);
@ -1847,6 +1847,15 @@ function coursemodule_visible_for_user($cm, $userid=0) {
if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_MODULE, $cm->id), $userid)) {
return false;
}
if ($CFG->enableavailability) {
require_once($CFG->libdir.'/conditionlib.php');
$ci=new condition_info($cm,CONDITION_MISSING_EXTRATABLE);
if(!$ci->is_available($cm->availableinfo,false,$userid) and
!has_capability('moodle/course:viewhiddenactivities',
get_context_instance(CONTEXT_MODULE, $cm->id), $userid)) {
return false;
}
}
return groups_course_module_visible($cm, $userid);
}

View File

@ -171,8 +171,8 @@
<FIELD NAME="sourcecmid" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="If this condition is based on completion of another activity, then this is the course-module ID of that activity. Otherwise null." PREVIOUS="coursemoduleid" NEXT="requiredcompletion"/>
<FIELD NAME="requiredcompletion" TYPE="int" LENGTH="1" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="If this condition is on a module's completion, then this should be set to the required completion state. Otherwise null. Suitable values are 1 = completed, 2 = completed-passed, 3 = completed-failed." PREVIOUS="sourcecmid" NEXT="gradeitemid"/>
<FIELD NAME="gradeitemid" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="If this condition is based on a gradebook score, the item ID is given here (and the item will now not be available until a value is achieved for that grade). Otherwise null." PREVIOUS="requiredcompletion" NEXT="grademin"/>
<FIELD NAME="grademin" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" ENUM="false" DECIMALS="5" COMMENT="If set, this is the minimum grade that must be reached in order for this module to appear. Otherwise null." PREVIOUS="gradeitemid" NEXT="grademax"/>
<FIELD NAME="grademax" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" ENUM="false" DECIMALS="5" COMMENT="If set, this is the maximum grade that can be reached in order to display this item. Otherwise null." PREVIOUS="grademin"/>
<FIELD NAME="grademin" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" ENUM="false" DECIMALS="5" COMMENT="If set, this is the minimum grade that must be reached (greater than or equal) in order for this module to appear. Otherwise null." PREVIOUS="gradeitemid" NEXT="grademax"/>
<FIELD NAME="grademax" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" ENUM="false" DECIMALS="5" COMMENT="If set, this is the maximum grade that users must be below (less than) in order to display this item. Otherwise null." PREVIOUS="grademin"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="coursemoduleid"/>

View File

@ -1056,60 +1056,6 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint($result, 2008111801);
}
if ($result && $oldversion < 2008112400) {
/// Define field availablefrom to be added to course_modules
$table = new xmldb_table('course_modules');
$field = new xmldb_field('availablefrom', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'completionexpected');
/// Conditionally launch add field availablefrom
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field availableuntil to be added to course_modules
$field = new xmldb_field('availableuntil', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'availablefrom');
/// Conditionally launch add field availableuntil
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field showavailability to be added to course_modules
$field = new xmldb_field('showavailability', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'availableuntil');
/// Conditionally launch add field showavailability
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define table course_modules_availability to be created
$table = new xmldb_table('course_modules_availability');
/// Adding fields to table course_modules_availability
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
$table->add_field('coursemoduleid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
$table->add_field('sourcecmid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null);
$table->add_field('requiredcompletion', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, null, null, null, null, null);
$table->add_field('gradeitemid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null);
$table->add_field('grademin', XMLDB_TYPE_NUMBER, '10, 5', null, null, null, null, null, null);
$table->add_field('grademax', XMLDB_TYPE_NUMBER, '10, 5', null, null, null, null, null, null);
/// Adding keys to table course_modules_availability
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('coursemoduleid', XMLDB_KEY_FOREIGN, array('coursemoduleid'), 'course_modules', array('id'));
$table->add_key('sourcecmid', XMLDB_KEY_FOREIGN, array('sourcecmid'), 'course_modules', array('id'));
$table->add_key('gradeitemid', XMLDB_KEY_FOREIGN, array('gradeitemid'), 'grade_items', array('id'));
/// Conditionally launch create table for course_modules_availability
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
/// Main savepoint reached
upgrade_main_savepoint($result, 2008112400);
}
if ($result && $oldversion < 2008120700) {
/// Changing precision of field shortname on table course_request to (100)
@ -1155,6 +1101,70 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint($result, 2008120801);
}
if ($result && $oldversion < 2008121701) {
/// Define field availablefrom to be added to course_modules
$table = new xmldb_table('course_modules');
$field = new xmldb_field('availablefrom', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'completionexpected');
/// Conditionally launch add field availablefrom
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field availableuntil to be added to course_modules
$field = new xmldb_field('availableuntil', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'availablefrom');
/// Conditionally launch add field availableuntil
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field showavailability to be added to course_modules
$field = new xmldb_field('showavailability', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'availableuntil');
/// Conditionally launch add field showavailability
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define table course_modules_availability to be created
$table = new xmldb_table('course_modules_availability');
/// Adding fields to table course_modules_availability
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
$table->add_field('coursemoduleid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
$table->add_field('sourcecmid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null);
$table->add_field('requiredcompletion', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, null, null, null, null, null);
$table->add_field('gradeitemid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null);
$table->add_field('grademin', XMLDB_TYPE_NUMBER, '10, 5', null, null, null, null, null, null);
$table->add_field('grademax', XMLDB_TYPE_NUMBER, '10, 5', null, null, null, null, null, null);
/// Adding keys to table course_modules_availability
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('coursemoduleid', XMLDB_KEY_FOREIGN, array('coursemoduleid'), 'course_modules', array('id'));
$table->add_key('sourcecmid', XMLDB_KEY_FOREIGN, array('sourcecmid'), 'course_modules', array('id'));
$table->add_key('gradeitemid', XMLDB_KEY_FOREIGN, array('gradeitemid'), 'grade_items', array('id'));
/// Conditionally launch create table for course_modules_availability
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
/// Changes to modinfo mean we need to rebuild course cache
rebuild_course_cache(0,true);
/// For developer upgrades, turn on the conditional activities and completion
/// features automatically (to gain more testing)
if(debugging('',DEBUG_DEVELOPER)) {
set_config('enableavailability',1);
set_config('enablecompletion',1);
}
/// Main savepoint reached
upgrade_main_savepoint($result, 2008121701);
}
return $result;
}

View File

@ -738,10 +738,16 @@ class grade_grade extends grade_object {
/**
* Used to notify the completion system (if necessary) that a user's grade
* has changed.
* has changed, and clear up a possible score cache.
* @param bool deleted True if grade was actually deleted
*/
function notify_changed($deleted) {
// Grades may be cached in user session
global $USER,$SESSION;
if($USER->id==$this->userid) {
unset($SESSION->gradescorecache[$this->itemid]);
}
// Ignore during restore
// TODO There should be a proper way to determine when we are in restore
// so that this hack looking for a $restore global is not needed.

View File

@ -2047,6 +2047,30 @@ function require_login($courseorid=0, $autologinguest=true, $cm=null, $setwantsu
print_error('nocontext');
}
}
// Conditional activity access control
if(!empty($CFG->enableavailability) and $cm) {
// We cache conditional access in session
if(!isset($SESSION->conditionaccessok)) {
$SESSION->conditionaccessok=array();
}
// If you have been allowed into the module once then you are allowed
// in for rest of session, no need to do conditional checks
if(!array_key_exists($cm->id,$SESSION->conditionaccessok)) {
// Get condition info (does a query for the availability table)
require_once($CFG->libdir.'/conditionlib.php');
$ci=new condition_info($cm,CONDITION_MISSING_EXTRATABLE);
// Check condition for user (this will do a query if the availability
// information depends on grade or completion information)
if($ci->is_available($junk) ||
has_capability('moodle/course:viewhiddenactivities', $COURSE->context)) {
$SESSION->conditionaccessok[$cm->id]=true;
} else {
print_error('activityiscurrentlyhidden');
}
}
}
if ($COURSE->id == SITEID) {
/// Eliminate hidden site activities straight away
if (!empty($cm) && !$cm->visible

View File

@ -0,0 +1,344 @@
<?php
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.');
}
require_once($CFG->dirroot . '/lib/conditionlib.php');
class conditionlib_test extends MoodleUnitTestCase {
var $oldcfg;
public function setUp() {
parent::setUp();
global $CFG;
$this->oldcfg=clone $CFG;
$CFG->enableavailability=true;
$CFG->enablecompletion=true;
}
/**
* Method called after each test method. Doesn't do anything extraordinary except restore the global $DB to the real one.
*/
public function tearDown() {
$CFG->enableavailability=$this->oldcfg->enableavailability;
$CFG->enablecompletion=$this->oldcfg->enablecompletion;
parent::tearDown();
}
function test_constructor() {
global $DB,$CFG;
$cm=new stdClass;
// Test records
$id=$DB->insert_record('course_modules',(object)array(
'showavailability'=>1,'availablefrom'=>17,'availableuntil'=>398,'course'=>64));
// no ID
try {
$test=new condition_info($cm);
$this->fail();
} catch(coding_exception $e) {
}
// no other data
$cm->id=$id;
$test=new condition_info($cm,CONDITION_MISSING_EVERYTHING);
$this->assertEqual(
(object)array('id'=>$id,'showavailability'=>1,
'availablefrom'=>17,'availableuntil'=>398,'course'=>64,
'conditionsgrade'=>array(), 'conditionscompletion'=>array()),
$test->get_full_course_module());
// just the course_modules stuff; check it doesn't request that from db
$cm->showavailability=0;
$cm->availablefrom=2;
$cm->availableuntil=74;
$cm->course=38;
$test=new condition_info($cm,CONDITION_MISSING_EXTRATABLE);
$this->assertEqual(
(object)array('id'=>$id,'showavailability'=>0,
'availablefrom'=>2,'availableuntil'=>74,'course'=>38,
'conditionsgrade'=>array(), 'conditionscompletion'=>array()),
$test->get_full_course_module());
// Now let's add some actual grade/completion conditions
$DB->insert_record('course_modules_availability',(object)array(
'coursemoduleid'=>$id,
'sourcecmid'=>42,
'requiredcompletion'=>2
));
$DB->insert_record('course_modules_availability',(object)array(
'coursemoduleid'=>$id,
'sourcecmid'=>666,
'requiredcompletion'=>1
));
$DB->insert_record('course_modules_availability',(object)array(
'coursemoduleid'=>$id,
'gradeitemid'=>37,
'grademin'=>5.5
));
$cm=(object)array('id'=>$id);
$test=new condition_info($cm,CONDITION_MISSING_EVERYTHING);
$fullcm=$test->get_full_course_module();
$this->assertEqual(array(42=>2,666=>1),$fullcm->conditionscompletion);
$this->assertEqual(array(37=>(object)array('min'=>5.5,'max'=>null,'name'=>'!missing')),
$fullcm->conditionsgrade);
}
private function make_course() {
global $DB;
$categoryid=$DB->insert_record('course_categories',(object)array());
return $DB->insert_record('course',(object)array(
'fullname'=>'Condition test','shortname'=>'CT1',
'category'=>$categoryid,'enablecompletion'=>1));
}
private function make_course_module($courseid,$params=array()) {
global $DB;
static $moduleid=0;
if(!$moduleid) {
$moduleid=$DB->get_field('modules','id',array('name'=>'resource'));
}
$rid=$DB->insert_record('resource',(object)array('course'=>$courseid,
'name'=>'xxx','alltext'=>'','popup'=>''));
$settings=(object)array(
'course'=>$courseid,'module'=>$moduleid,'instance'=>$rid);
foreach($params as $name=>$value) {
$settings->{$name}=$value;
}
return $DB->insert_record('course_modules',$settings);
}
private function make_section($courseid,$cmids,$sectionnum=0) {
global $DB;
$DB->insert_record('course_sections',(object)array(
'course'=>$courseid,'sequence'=>implode(',',$cmids),'section'=>$sectionnum));
}
function test_modinfo() {
global $DB;
// Let's make a course
$courseid=$this->make_course();
// Now let's make a couple modules on that course
$cmid1=$this->make_course_module($courseid,array(
'showavailability'=>1,'availablefrom'=>17,'availableuntil'=>398,
'completion'=>COMPLETION_TRACKING_MANUAL));
$cmid2=$this->make_course_module($courseid,array(
'showavailability'=>0,'availablefrom'=>0,'availableuntil'=>0));
$this->make_section($courseid,array($cmid1,$cmid2));
// Add a fake grade item
$gradeitemid=$DB->insert_record('grade_items',(object)array(
'courseid'=>$courseid,'itemname'=>'frog'));
// One of the modules has grade and completion conditions, other doesn't
$DB->insert_record('course_modules_availability',(object)array(
'coursemoduleid'=>$cmid2,
'sourcecmid'=>$cmid1,
'requiredcompletion'=>1
));
$DB->insert_record('course_modules_availability',(object)array(
'coursemoduleid'=>$cmid2,
'gradeitemid'=>$gradeitemid,
'grademin'=>5.5
));
// Okay sweet, now get modinfo
$modinfo=get_fast_modinfo($DB->get_record('course',array('id'=>$courseid)));
// Test basic data
$this->assertEqual(1,$modinfo->cms[$cmid1]->showavailability);
$this->assertEqual(17,$modinfo->cms[$cmid1]->availablefrom);
$this->assertEqual(398,$modinfo->cms[$cmid1]->availableuntil);
$this->assertEqual(0,$modinfo->cms[$cmid2]->showavailability);
$this->assertEqual(0,$modinfo->cms[$cmid2]->availablefrom);
$this->assertEqual(0,$modinfo->cms[$cmid2]->availableuntil);
// Test condition arrays
$this->assertEqual(array(),$modinfo->cms[$cmid1]->conditionscompletion);
$this->assertEqual(array(),$modinfo->cms[$cmid1]->conditionsgrade);
$this->assertEqual(array($cmid1=>1),
$modinfo->cms[$cmid2]->conditionscompletion);
$this->assertEqual(array($gradeitemid=>(object)array('min'=>5.5,'max'=>null,'name'=>'frog')),
$modinfo->cms[$cmid2]->conditionsgrade);
}
function test_add_and_remove() {
global $DB;
// Make course and module
$courseid=$this->make_course();
$cmid=$this->make_course_module($courseid,array(
'showavailability'=>0,'availablefrom'=>0,'availableuntil'=>0));
$this->make_section($courseid,array($cmid));
// Check it has no conditions
$test1=new condition_info((object)array('id'=>$cmid),
CONDITION_MISSING_EVERYTHING);
$cm=$test1->get_full_course_module();
$this->assertEqual(array(),$cm->conditionscompletion);
$this->assertEqual(array(),$cm->conditionsgrade);
// Add conditions of each type
$test1->add_completion_condition(13,3);
$this->assertEqual(array(13=>3),$cm->conditionscompletion);
$test1->add_grade_condition(666,0.4,null,true);
$this->assertEqual(array(666=>(object)array('min'=>0.4,'max'=>null,'name'=>'!missing')),
$cm->conditionsgrade);
// Check they were really added in db
$test2=new condition_info((object)array('id'=>$cmid),
CONDITION_MISSING_EVERYTHING);
$cm=$test2->get_full_course_module();
$this->assertEqual(array(13=>3),$cm->conditionscompletion);
$this->assertEqual(array(666=>(object)array('min'=>0.4,'max'=>null,'name'=>'!missing')),
$cm->conditionsgrade);
// Wipe conditions
$test2->wipe_conditions();
$this->assertEqual(array(),$cm->conditionscompletion);
$this->assertEqual(array(),$cm->conditionsgrade);
// Check they were really wiped
$test3=new condition_info((object)array('id'=>$cmid),
CONDITION_MISSING_EVERYTHING);
$cm=$test3->get_full_course_module();
$this->assertEqual(array(),$cm->conditionscompletion);
$this->assertEqual(array(),$cm->conditionsgrade);
}
function test_is_available() {
global $DB,$USER;
$courseid=$this->make_course();
// No conditions
$cmid=$this->make_course_module($courseid);
$ci=new condition_info((object)array('id'=>$cmid),
CONDITION_MISSING_EVERYTHING);
$this->assertTrue($ci->is_available($text,false,0));
$this->assertEqual('',$text);
// Time (from)
$time=time()+100;
$cmid=$this->make_course_module($courseid,array('availablefrom'=>$time));
$ci=new condition_info((object)array('id'=>$cmid),
CONDITION_MISSING_EVERYTHING);
$this->assertFalse($ci->is_available($text));
$this->assert(new PatternExpectation(
'/'.preg_quote(userdate($time,get_string('strftimedate','langconfig'))).'/'),$text);
$time=time()-100;
$cmid=$this->make_course_module($courseid,array('availablefrom'=>$time));
$ci=new condition_info((object)array('id'=>$cmid),
CONDITION_MISSING_EVERYTHING);
$this->assertTrue($ci->is_available($text));
$this->assertEqual('',$text);
$this->assert(new PatternExpectation(
'/'.preg_quote(userdate($time,get_string('strftimedate','langconfig'))).'/'),$ci->get_full_information());
// Time (until)
$cmid=$this->make_course_module($courseid,array('availableuntil'=>time()-100));
$ci=new condition_info((object)array('id'=>$cmid),
CONDITION_MISSING_EVERYTHING);
$this->assertFalse($ci->is_available($text));
$this->assertEqual('',$text);
// Completion
$oldid=$cmid;
$cmid=$this->make_course_module($courseid);
$this->make_section($courseid,array($oldid,$cmid));
$oldcm=$DB->get_record('course_modules',array('id'=>$oldid));
$oldcm->completion=COMPLETION_TRACKING_MANUAL;
$DB->update_record('course_modules',$oldcm);
$ci=new condition_info((object)array('id'=>$cmid),CONDITION_MISSING_EVERYTHING);
$ci->add_completion_condition($oldid,COMPLETION_COMPLETE);
$this->assertFalse($ci->is_available($text,false));
$this->assertEqual(get_string('requires_completion_1','condition','xxx'),$text);
$completion=new completion_info($DB->get_record('course',array('id'=>$courseid)));
$completion->update_state($oldcm,COMPLETION_COMPLETE);
completion_info::wipe_session_cache();
condition_info::wipe_session_cache();
$this->assertTrue($ci->is_available($text));
$this->assertFalse($ci->is_available($text,false,$USER->id+1));
completion_info::wipe_session_cache();
condition_info::wipe_session_cache();
$completion=new completion_info($DB->get_record('course',array('id'=>$courseid)));
$completion->update_state($oldcm,COMPLETION_INCOMPLETE);
$this->assertFalse($ci->is_available($text));
$ci->wipe_conditions();
$ci->add_completion_condition($oldid,COMPLETION_INCOMPLETE);
condition_info::wipe_session_cache();
$this->assertTrue($ci->is_available($text));
$this->assertTrue($ci->is_available($text,false,$USER->id+1));
condition_info::wipe_session_cache();
$this->assertTrue($ci->is_available($text,true));
// Grade
$ci->wipe_conditions();
// Add a fake grade item
$gradeitemid=$DB->insert_record('grade_items',(object)array(
'courseid'=>$courseid,'itemname'=>'frog'));
// Add a condition on a value existing...
$ci->add_grade_condition($gradeitemid,null,null,true);
$this->assertFalse($ci->is_available($text));
$this->assertEqual(get_string('requires_grade_any','condition','frog'),$text);
// Fake it existing
$DB->insert_record('grade_grades',(object)array(
'itemid'=>$gradeitemid,'userid'=>$USER->id,'finalgrade'=>3.78));
condition_info::wipe_session_cache();
$this->assertTrue($ci->is_available($text));
condition_info::wipe_session_cache();
$this->assertTrue($ci->is_available($text,true));
// Now require that user gets more than 3.78001
$ci->wipe_conditions();
$ci->add_grade_condition($gradeitemid,3.78001,null,true);
condition_info::wipe_session_cache();
$this->assertFalse($ci->is_available($text));
$this->assertEqual(get_string('requires_grade_min','condition','frog'),$text);
// ...just on 3.78...
$ci->wipe_conditions();
$ci->add_grade_condition($gradeitemid,3.78,null,true);
condition_info::wipe_session_cache();
$this->assertTrue($ci->is_available($text));
// ...less than 3.78
$ci->wipe_conditions();
$ci->add_grade_condition($gradeitemid,null,3.78,true);
condition_info::wipe_session_cache();
$this->assertFalse($ci->is_available($text));
$this->assertEqual(get_string('requires_grade_max','condition','frog'),$text);
// ...less than 3.78001
$ci->wipe_conditions();
$ci->add_grade_condition($gradeitemid,null,3.78001,true);
condition_info::wipe_session_cache();
$this->assertTrue($ci->is_available($text));
// ...in a range that includes it
$ci->wipe_conditions();
$ci->add_grade_condition($gradeitemid,3,4,true);
condition_info::wipe_session_cache();
$this->assertTrue($ci->is_available($text));
// ...in a range that doesn't include it
$ci->wipe_conditions();
$ci->add_grade_condition($gradeitemid,4,5,true);
condition_info::wipe_session_cache();
$this->assertFalse($ci->is_available($text));
$this->assertEqual(get_string('requires_grade_range','condition','frog'),$text);
}
}
?>

View File

@ -569,6 +569,19 @@ h2.headingblock {
color: #666666;
}
#course-view .availabilityinfo {
font-size:0.85em;
color:#aaa;
}
#course-view .availabilityinfo strong {
font-weight:normal;
color:black;
}
#course-view .dimmed_text img {
opacity:0.3;
filter: alpha(opacity='30');
}
/***
*** Doc
***/
@ -1057,6 +1070,10 @@ body#mod-forum-index .generalbox .cell {
#mod-quiz-edit .questionbankwindow div.header{
font-weight:bold;
}
#mod-quiz-edit a.configurerandomquestion{
font-size:small;
text-decoration:underline;
}
/***

View File

@ -6,7 +6,7 @@
// This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php)
$version = 2008121000; // YYYYMMDD = date of the last version bump
$version = 2008121701; // YYYYMMDD = date of the last version bump
// XX = daily increments
$release = '2.0 dev (Build: 20081217)'; // Human-friendly version name