$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
$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));
}
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
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
$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
}
}
+ // 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;
}
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);
}
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;
}
}
}
}
+
+ 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)
// 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();
$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";
*/
function &get_fast_modinfo(&$course, $userid=0) {
global $CFG, $USER, $DB;
+ if(!empty($CFG->enableavailability)) {
+ require_once($CFG->libdir.'/conditionlib.php');
+ }
static $cache = array();
$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])) {
}
$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)
* 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!)
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;
}
if (isset($modinfo->cms[$modnumber])) {
- if (!$modinfo->cms[$modnumber]->uservisible) {
+ if (!$modinfo->cms[$modnumber]->uservisible &&
+ empty($modinfo->cms[$modnumber]->showavailability)) {
// visibility shortcut
continue;
}
// module not installed
continue;
}
- if (!coursemodule_visible_for_user($mod)) {
+ if (!coursemodule_visible_for_user($mod) &&
+ empty($mod->showavailability)) {
// full visibility check
continue;
}
$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);
$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>';
-
- 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);
+ // 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);
+ }
+ 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') {
$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) {
}
}
+ // 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";
}
print_error('cannotupdatelevel');
}
+ rebuild_course_cache($cm->course);
+
if (SITEID == $cm->course) {
redirect($CFG->wwwroot);
} else {
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);
$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))) {
$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');
$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');
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");
<?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
$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
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);
}
/**
$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'));
$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')));
--- /dev/null
+<?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 ≥';
+$string['grade_upto']='and <';
+$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: ‘$a’';
+$string['userrestriction_hidden']='Activity conditionally restricted (completely hidden, no message): ‘$a’';
+?>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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 (≥), a maximum value (<), 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>
--- /dev/null
+<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>
* 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
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) {
}
/**
- * 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);
}
}
--- /dev/null
+<?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']);
+ }
+ }
+ }
+ }
+}
+?>
* @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);
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);
}
<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"/>
upgrade_main_savepoint($result, 2008111801);
}
- if ($result && $oldversion < 2008112400) {
+ if ($result && $oldversion < 2008120700) {
+
+ /// Changing precision of field shortname on table course_request to (100)
+ $table = new xmldb_table('course_request');
+ $field = new xmldb_field('shortname', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, null, null, 'fullname');
+
+ /// Launch change of precision for field shortname
+ $dbman->change_field_precision($table, $field);
+
+ /// Main savepoint reached
+ upgrade_main_savepoint($result, 2008120700);
+ }
+
+ /// For MDL-17501. Ensure that any role that has moodle/course:update also
+ /// has moodle/course:visibility.
+ if ($result && $oldversion < 2008120800) {
+ /// Get the roles with 'moodle/course:update'.
+ $systemcontext = get_context_instance(CONTEXT_SYSTEM);
+ $roles = get_roles_with_capability('moodle/course:update', CAP_ALLOW, $systemcontext);
+
+ /// Give those roles 'moodle/course:visibility'.
+ foreach ($roles as $role) {
+ assign_capability('moodle/course:visibility', CAP_ALLOW, $role->id, $systemcontext->id);
+ }
+
+ /// Force all sessions to refresh access data.
+ mark_context_dirty($systemcontext->path);
+
+ /// Main savepoint reached
+ upgrade_main_savepoint($result, 2008120800);
+ }
+
+ if ($result && $oldversion < 2008120801) {
+
+ /// Changing precision of field shortname on table mnet_enrol_course to (100)
+ $table = new xmldb_table('mnet_enrol_course');
+ $field = new xmldb_field('shortname', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, null, null, 'fullname');
+
+ /// Launch change of precision for field shortname
+ $dbman->change_field_precision($table, $field);
+
+ /// Main savepoint reached
+ upgrade_main_savepoint($result, 2008120801);
+ }
+
+ if ($result && $oldversion < 2008121701) {
/// Define field availablefrom to be added to course_modules
$table = new xmldb_table('course_modules');
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');
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');
$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)
- $table = new xmldb_table('course_request');
- $field = new xmldb_field('shortname', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, null, null, 'fullname');
-
- /// Launch change of precision for field shortname
- $dbman->change_field_precision($table, $field);
-
- /// Main savepoint reached
- upgrade_main_savepoint($result, 2008120700);
- }
-
- /// For MDL-17501. Ensure that any role that has moodle/course:update also
- /// has moodle/course:visibility.
- if ($result && $oldversion < 2008120800) {
- /// Get the roles with 'moodle/course:update'.
- $systemcontext = get_context_instance(CONTEXT_SYSTEM);
- $roles = get_roles_with_capability('moodle/course:update', CAP_ALLOW, $systemcontext);
+ /// Changes to modinfo mean we need to rebuild course cache
+ rebuild_course_cache(0,true);
- /// Give those roles 'moodle/course:visibility'.
- foreach ($roles as $role) {
- assign_capability('moodle/course:visibility', CAP_ALLOW, $role->id, $systemcontext->id);
+ /// 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);
}
- /// Force all sessions to refresh access data.
- mark_context_dirty($systemcontext->path);
-
- /// Main savepoint reached
- upgrade_main_savepoint($result, 2008120800);
- }
-
- if ($result && $oldversion < 2008120801) {
-
- /// Changing precision of field shortname on table mnet_enrol_course to (100)
- $table = new xmldb_table('mnet_enrol_course');
- $field = new xmldb_field('shortname', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, null, null, 'fullname');
-
- /// Launch change of precision for field shortname
- $dbman->change_field_precision($table, $field);
-
/// Main savepoint reached
- upgrade_main_savepoint($result, 2008120801);
+ upgrade_main_savepoint($result, 2008121701);
}
return $result;
/**
* 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.
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
--- /dev/null
+<?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);
+ }
+
+}
+?>
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
***/
#mod-quiz-edit .questionbankwindow div.header{
font-weight:bold;
}
+#mod-quiz-edit a.configurerandomquestion{
+ font-size:small;
+ text-decoration:underline;
+}
/***
// 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