Index: lib/completionlib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/completionlib.php,v
retrieving revision 1.10
diff -u -r1.10 completionlib.php
--- lib/completionlib.php 26 Nov 2008 13:59:24 -0000 1.10
+++ lib/completionlib.php 5 Dec 2008 16:52:22 -0000
@@ -394,13 +394,15 @@
* 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
@@ -716,22 +718,24 @@
}
/**
- * 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);
}
}
Index: lib/moodlelib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/moodlelib.php,v
retrieving revision 1.1131
diff -u -r1.1131 moodlelib.php
--- lib/moodlelib.php 1 Dec 2008 07:11:33 -0000 1.1131
+++ lib/moodlelib.php 5 Dec 2008 16:52:23 -0000
@@ -2047,6 +2047,30 @@
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
Index: lib/datalib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/datalib.php,v
retrieving revision 1.493
diff -u -r1.493 datalib.php
--- lib/datalib.php 4 Dec 2008 08:53:11 -0000 1.493
+++ lib/datalib.php 5 Dec 2008 16:52:22 -0000
@@ -1835,7 +1835,7 @@
* @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 @@
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);
}
Index: course/modedit.php
===================================================================
RCS file: /cvsroot/moodle/moodle/course/modedit.php,v
retrieving revision 1.59
diff -u -r1.59 modedit.php
--- course/modedit.php 2 Nov 2008 21:37:41 -0000 1.59
+++ course/modedit.php 5 Dec 2008 16:52:21 -0000
@@ -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");
Index: course/moodleform_mod.php
===================================================================
RCS file: /cvsroot/moodle/moodle/course/moodleform_mod.php,v
retrieving revision 1.36
diff -u -r1.36 moodleform_mod.php
--- course/moodleform_mod.php 13 Oct 2008 22:43:26 -0000 1.36
+++ course/moodleform_mod.php 5 Dec 2008 16:52:22 -0000
@@ -1,6 +1,11 @@
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,31 @@
$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);
+ $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 +260,9 @@
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 +383,90 @@
$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 +475,7 @@
$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')));
Index: course/mod.php
===================================================================
RCS file: /cvsroot/moodle/moodle/course/mod.php,v
retrieving revision 1.136
diff -u -r1.136 mod.php
--- course/mod.php 27 Aug 2008 15:11:20 -0000 1.136
+++ course/mod.php 5 Dec 2008 16:52:21 -0000
@@ -172,6 +172,8 @@
print_error('cannotupdatelevel');
}
+ rebuild_course_cache($cm->course);
+
if (SITEID == $cm->course) {
redirect($CFG->wwwroot);
} else {
Index: course/lib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/course/lib.php,v
retrieving revision 1.624
diff -u -r1.624 lib.php
--- course/lib.php 5 Dec 2008 08:56:54 -0000 1.624
+++ course/lib.php 5 Dec 2008 16:52:21 -0000
@@ -982,6 +982,9 @@
// 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 @@
$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_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 @@
$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 @@
}
$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 @@
* 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 @@
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 @@
}
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 @@
// 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 @@
$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 "
";
}
echo format_text($extra, FORMAT_HTML, $labelformatoptions);
@@ -1416,17 +1477,27 @@
$altname = get_accesshide(' '.$altname);
}
- $linkcss = $mod->visible ? "" : " class=\"dimmed\" ";
- echo '
'.
- '
'.
- $instancename.$altname.'';
-
- 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 '
'.
+ '
'.
+ $instancename.$altname.'';
+
+ 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 "
(".format_string($groupings[$mod->groupingid]->name).')';
}
- echo "
(".format_string($groupings[$mod->groupingid]->name).')';
+ } else {
+ // Display greyed-out text of link
+ echo '
'.
+ '
'.
+ $instancename.$altname.'';
}
}
if ($usetracking && $mod->modname == 'forum') {
@@ -1460,7 +1531,8 @@
$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 @@
}
}
+ // Show availability information (for someone who isn't allowed to
+ // see the activity itself, or for staff)
+ if(!$mod->uservisible) {
+ echo '
'.$mod->availableinfo.'
';
+ } else if($isediting && !empty($CFG->enableavailability)) {
+ $ci = new condition_info($mod);
+ $fullinfo=$ci->get_full_information();
+ if($fullinfo) {
+ echo '
'.get_string($mod->showavailability
+ ? 'userrestriction_visible'
+ : 'userrestriction_hidden','condition',
+ $fullinfo).'
';
+ }
+ }
+
echo "\n";
}
Index: lib/db/upgrade.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/db/upgrade.php,v
retrieving revision 1.251
diff -u -r1.251 upgrade.php
--- lib/db/upgrade.php 24 Nov 2008 17:21:30 -0000 1.251
+++ lib/db/upgrade.php 5 Dec 2008 16:52:24 -0000
@@ -1056,7 +1056,7 @@
upgrade_main_savepoint($result, 2008111801);
}
- if ($result && $oldversion < 2008112400) {
+ if ($result && $oldversion < 2008120401) {
/// Define field availablefrom to be added to course_modules
$table = new xmldb_table('course_modules');
@@ -1106,8 +1106,11 @@
$dbman->create_table($table);
}
+ /// Changes to modinfo mean we need to rebuild course cache
+ rebuild_course_cache(0,true);
+
/// Main savepoint reached
- upgrade_main_savepoint($result, 2008112400);
+ upgrade_main_savepoint($result, 2008120401);
}
Index: lib/db/install.xml
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/db/install.xml,v
retrieving revision 1.177
diff -u -r1.177 install.xml
--- lib/db/install.xml 24 Nov 2008 17:21:30 -0000 1.177
+++ lib/db/install.xml 5 Dec 2008 16:52:24 -0000
@@ -175,8 +175,8 @@
-
-
+
+
Index: admin/settings/development.php
===================================================================
RCS file: /cvsroot/moodle/moodle/admin/settings/development.php,v
retrieving revision 1.1
diff -u -r1.1 development.php
--- admin/settings/development.php 8 Sep 2008 11:32:32 -0000 1.1
+++ admin/settings/development.php 5 Dec 2008 16:52:19 -0000
@@ -15,9 +15,9 @@
$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')));
+ $temp->add(new admin_setting_configcheckbox('enablecompletion', get_string('enablecompletion','completion'), get_string('configenablecompletion','completion'), 0));
+ $temp->add(new admin_setting_pickroles('progresstrackedroles', get_string('progresstrackedroles','completion'), get_string('configprogresstrackedroles', 'completion'), array('moodle/legacy:student')));
+ $temp->add(new admin_setting_configcheckbox('enableavailability', get_string('enableavailability','condition'), get_string('configenableavailability','condition'), 0));
$ADMIN->add('experimental', $temp);
Index: lib/grade/grade_grade.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/grade/grade_grade.php,v
retrieving revision 1.45
diff -u -r1.45 grade_grade.php
--- lib/grade/grade_grade.php 28 Jul 2008 15:58:51 -0000 1.45
+++ lib/grade/grade_grade.php 5 Dec 2008 16:52:24 -0000
@@ -738,10 +738,16 @@
/**
* 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.
Index: version.php
===================================================================
RCS file: /cvsroot/moodle/moodle/version.php,v
retrieving revision 1.906
diff -u -r1.906 version.php
--- version.php 4 Dec 2008 23:52:23 -0000 1.906
+++ version.php 5 Dec 2008 16:52:19 -0000
@@ -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 = 2008120400; // YYYYMMDD = date of the last version bump
+ $version = 2008120401; // YYYYMMDD = date of the last version bump
// XX = daily increments
$release = '2.0 dev (Build: 20081205)'; // Human-friendly version name
Index: backup/restorelib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/backup/restorelib.php,v
retrieving revision 1.353
diff -u -r1.353 restorelib.php
--- backup/restorelib.php 13 Nov 2008 08:40:59 -0000 1.353
+++ backup/restorelib.php 5 Dec 2008 16:52:21 -0000
@@ -1127,6 +1127,10 @@
$course_module->completionview=$mod->completionview;
$course_module->completionexpected=$mod->completionexpected;
+ $course_module->availablefrom=$mod->availablefrom;
+ $course_module->availableuntil=$mod->availableuntil;
+ $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 @@
}
}
+ // 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 "Can't find new ID for cm $data->coursemoduleid, ignoring availability condition.
";
+ }
+ $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 "Can't find new ID for source cm $data->sourcecmid, ignoring availability condition.
";
+ }
+ $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 "Can't find new ID for grade item $data->gradeitemid, ignoring availability condition.
";
+ }
+ $status=false;
+ continue;
+ }
+ }
+
+ // Add record
+ if(!$DB->insert_record('course_modules_availability',$data)) {
+ if (!defined('RESTORE_SILENTLY')) {
+ echo "Failed to insert availability data record.
";
+ }
+ $status=false;
+ continue;
+ }
+ }
+ }
} else {
$status = false;
}
@@ -5616,6 +5675,12 @@
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);
}
@@ -5667,6 +5732,15 @@
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;
}
@@ -5792,6 +5866,40 @@
}
}
}
+
+ 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)
Index: backup/backuplib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/backup/backuplib.php,v
retrieving revision 1.223
diff -u -r1.223 backuplib.php
--- backup/backuplib.php 16 Nov 2008 23:24:32 -0000 1.223
+++ backup/backuplib.php 5 Dec 2008 16:52:19 -0000
@@ -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
Index: theme/standard/styles_fonts.css
===================================================================
RCS file: /cvsroot/moodle/moodle/theme/standard/styles_fonts.css,v
retrieving revision 1.169
diff -u -r1.169 styles_fonts.css
--- theme/standard/styles_fonts.css 28 Nov 2008 15:43:10 -0000 1.169
+++ theme/standard/styles_fonts.css 5 Dec 2008 16:52:24 -0000
@@ -569,6 +569,19 @@
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
***/
Index: lang/en_utf8/help/condition/showavailability.html
===================================================================
RCS file: lang/en_utf8/help/condition/showavailability.html
diff -N lang/en_utf8/help/condition/showavailability.html
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lang/en_utf8/help/condition/showavailability.html 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,27 @@
+Unavailable activity display
+
+
+When an activity is unavailable due to the restrictions in this box, there are
+two possibilities:
+
+
+
+- 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.
+- The activity does not display to users at all.
+
+
+
+In both cases, once the activity becomes available, it displays as normal.
+
+
+
+- Users with the 'view hidden activities' capability can still see
+ unavailable activities, regardless of this setting. The informational text
+ always appears to them.
+- 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.
+
Index: lang/en_utf8/condition.php
===================================================================
RCS file: lang/en_utf8/condition.php
diff -N lang/en_utf8/condition.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lang/en_utf8/condition.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,35 @@
+$a is incomplete.';
+$string['requires_completion_1']='Not available until the activity $a is marked complete.';
+$string['requires_completion_2']='Not available until the activity $a is complete and passed.';
+$string['requires_completion_3']='Not available unless the activity $a 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 $a.';
+$string['requires_grade_min']='Not available until you achieve a required score in $a.';
+$string['requires_grade_max']='Not available unless you get an appropriate score in $a.';
+$string['requires_grade_range']='Not available unless you get a particular score in $a.';
+$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’';
+?>
Index: lib/conditionlib.php
===================================================================
RCS file: lib/conditionlib.php
diff -N lib/conditionlib.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/conditionlib.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,522 @@
+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($expectingmissingget_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($expectingmissingcm);
+ }
+ }
+
+ /**
+ * 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;
+
+ if($condition->id) {
+ require_once($CFG->libdir.'/gradelib.php');
+ $item=new grade_item;
+ grade_object::set_properties($item,$condition);
+ $minmax->name=$item->get_name();
+ } else {
+ $minmax->name='!missing'; // Ooops, missing grade
+ }
+ $cm->conditionsgrade[$condition->gradeitemid]=$minmax;
+ }
+ }
+ }
+ }
+
+ /**
+ * @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,
+ 'name'=>$DB->get_field('grade_items','itemname',
+ 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...
+ }
+ }
+
+ $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']);
+ }
+ }
+ }
+ }
+}
+?>
Index: lang/en_utf8/help/condition/completionexpected.html
===================================================================
RCS file: lang/en_utf8/help/condition/completionexpected.html
diff -N lang/en_utf8/help/condition/completionexpected.html
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lang/en_utf8/help/condition/completionexpected.html 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,23 @@
+Available dates
+
+
+Using the 'Only available from' and 'Only available to' 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 to' date. Students
+cannot access it outside those times, even if they guess the URL.
+
+
+
+By default both dates are disabled, meaning that the activity is available at
+any time (as long as the student can access the course).
+
+
+
+- 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.
+- The activity completely vanishes on the 'available to' date, even if
+ you've chosen to show information.
+- Setting 'Visible' to 'Hide' overrides these settings. If you set 'Visible'
+ to 'Hide', the activity is never available regardless of date.
+
Index: lang/en_utf8/help/condition/completioncondition.html
===================================================================
RCS file: lang/en_utf8/help/condition/completioncondition.html
diff -N lang/en_utf8/help/condition/completioncondition.html
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lang/en_utf8/help/condition/completioncondition.html 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,20 @@
+Activity completion condition
+
+
+You can set a condition based on whether the user has completed another
+activity.
+
+
+
+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.)
+
+
+
+You can add more than one completion condition. All conditions must be met in
+order for the activity to appear.
+
Index: lang/en_utf8/help/condition/gradecondition.html
===================================================================
RCS file: lang/en_utf8/help/condition/gradecondition.html
diff -N lang/en_utf8/help/condition/gradecondition.html
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lang/en_utf8/help/condition/gradecondition.html 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,31 @@
+Grade condition
+
+
+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.
+
+
+
+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.
+
+
+
+You can add more than one grade condition. All conditions must be met in order
+for the activity to appear.
+
+
+
+- The range numbers can be fractional (with up to five decimal places) if
+ necessary.
+- 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.
+- 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.
+
Index: lib/simpletest/testconditionlib.php
===================================================================
RCS file: lib/simpletest/testconditionlib.php
diff -N lib/simpletest/testconditionlib.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/simpletest/testconditionlib.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,344 @@
+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'=>null)),
+ $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)).'/'),$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)).'/'),$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);
+ }
+
+}
+?>