Merge branch 'MDL-29538_usercond' of git://github.com/markn86/moodle

This commit is contained in:
Sam Hemelryk
2012-07-09 11:44:39 +12:00
13 changed files with 615 additions and 16 deletions
+8 -1
View File
@@ -322,6 +322,8 @@ class backup_module_structure_step extends backup_structure_step {
$availinfo = new backup_nested_element('availability_info');
$availability = new backup_nested_element('availability', array('id'), array(
'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
$availability_field = new backup_nested_element('availability_field', array('id'), array(
'userfield', 'customfieldid', 'operator', 'value'));
// attach format plugin structure to $module element, only one allowed
$this->add_plugin_structure('format', $module, false);
@@ -333,9 +335,9 @@ class backup_module_structure_step extends backup_structure_step {
// Define the tree
$module->add_child($availinfo);
$availinfo->add_child($availability);
$availinfo->add_child($availability_field);
// Set the sources
$module->set_source_sql('
SELECT cm.*, m.version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber
FROM {course_modules} cm
@@ -344,6 +346,7 @@ class backup_module_structure_step extends backup_structure_step {
WHERE cm.id = ?', array(backup::VAR_MODID));
$availability->set_source_table('course_modules_availability', array('coursemoduleid' => backup::VAR_MODID));
$availability_field->set_source_table('course_modules_avail_fields', array('coursemoduleid' => backup::VAR_MODID));
// Define annotations
$module->annotate_ids('grouping', 'groupingid');
@@ -373,11 +376,15 @@ class backup_section_structure_step extends backup_structure_step {
// Add nested elements for _availability table
$avail = new backup_nested_element('availability', array('id'), array(
'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
$avail_field = new backup_nested_element('availability_field', array('id'), array(
'userfield', 'customfieldid', 'operator', 'value'));
$section->add_child($avail);
$section->add_child($avail_field);
// Define sources
$section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
$avail->set_source_table('course_sections_availability', array('coursesectionid' => backup::VAR_SECTIONID));
$avail_field->set_source_table('course_sections_avail_fields', array('coursesectionid' => backup::VAR_SECTIONID));
// Aliases
$section->set_source_alias('section', 'number');
+51 -2
View File
@@ -546,6 +546,7 @@ class restore_process_course_modules_availability extends restore_execution_step
$DB->insert_record('course_modules_availability', $availability);
}
}
$rs->close();
}
}
@@ -1030,6 +1031,7 @@ class restore_section_structure_step extends restore_structure_step {
$paths[] = $section;
if ($CFG->enableavailability) {
$paths[] = new restore_path_element('availability', '/section/availability');
$paths[] = new restore_path_element('availability_field', '/section/availability_field');
}
// Apply for 'format' plugins optional paths at section level
@@ -1133,6 +1135,29 @@ class restore_section_structure_step extends restore_structure_step {
$this->set_mapping('course_sections_availability', $newid, $newid);
}
public function process_availability_field($data) {
global $DB;
$data = (object)$data;
// Mark it is as passed by default
$passed = true;
// Ok, if it is a profile field we need to check it exists
if (!is_null($data->customfieldid)) {
if (!$DB->record_exists('user_info_field', array('id' => $data->customfieldid))) {
$passed = false;
}
}
if ($passed) {
// Create the object to insert into the database
$avail_field = new stdClass();
$avail_field->coursesectionid = $this->task->get_sectionid();
$avail_field->userfield = $data->userfield;
$avail_field->customfieldid = $data->customfieldid;
$avail_field->operator = $data->operator;
$avail_field->value = $data->value;
$DB->insert_record('course_sections_avail_fields', $avail_field);
}
}
protected function after_execute() {
// Add section related files, with 'course_section' itemid to match
$this->add_related_files('course', 'section', 'course_section');
@@ -2539,6 +2564,7 @@ class restore_module_structure_step extends restore_structure_step {
$paths[] = $module;
if ($CFG->enableavailability) {
$paths[] = new restore_path_element('availability', '/module/availability_info/availability');
$paths[] = new restore_path_element('availability_field', '/module/availability_info/availability_field');
}
// Apply for 'format' plugins optional paths at module level
@@ -2632,15 +2658,38 @@ class restore_module_structure_step extends restore_structure_step {
$DB->set_field('course_sections', 'sequence', $sequence, array('id' => $data->section));
}
protected function process_availability($data) {
$data = (object)$data;
// Simply going to store the whole availability record now, we'll process
// all them later in the final task (once all actvivities have been restored)
// all them later in the final task (once all activities have been restored)
// Let's call the low level one to be able to store the whole object
$data->coursemoduleid = $this->task->get_moduleid(); // Let add the availability cmid
restore_dbops::set_backup_ids_record($this->get_restoreid(), 'module_availability', $data->id, 0, null, $data);
}
protected function process_availability_field($data) {
global $DB;
$data = (object)$data;
// Mark it is as passed by default
$passed = true;
// Ok, if it is a profile field we need to check it exists
if (!is_null($data->customfieldid)) {
if (!$DB->record_exists('user_info_field', array('id' => $data->customfieldid))) {
$passed = false;
}
}
if ($passed) {
// Create the object to insert into the database
$avail_field = new stdClass();
$avail_field->coursemoduleid = $this->task->get_moduleid(); // Lets add the availability cmid
$avail_field->userfield = $data->userfield;
$avail_field->customfieldid = $data->customfieldid;
$avail_field->operator = $data->operator;
$avail_field->value = $data->value;
// Insert into the database
$DB->insert_record('course_modules_avail_fields', $avail_field);
}
}
}
/**
+1
View File
@@ -54,6 +54,7 @@ if (!empty($CFG->enableavailability)) {
$sectioninfo = $modinfo->get_section_info($section->section);
$section->conditionsgrade = $sectioninfo->conditionsgrade;
$section->conditionscompletion = $sectioninfo->conditionscompletion;
$section->conditionsfield = $sectioninfo->conditionsfield;
}
$mform = new editsection_form($PAGE->url, array('course' => $course, 'editoroptions' => $editoroptions,
+54 -2
View File
@@ -38,6 +38,8 @@ class editsection_form extends moodleform {
$course = $this->_customdata['course'];
if (!empty($CFG->enableavailability)) {
// String used by conditions more than once
$strcondnone = get_string('none', 'condition');
// Grouping conditions - only if grouping is enabled at site level
if (!empty($CFG->enablegroupmembersonly)) {
$options = array();
@@ -68,7 +70,7 @@ class editsection_form extends moodleform {
$gradeoptions[$id] = $item->get_name();
}
asort($gradeoptions);
$gradeoptions = array(0 => get_string('none', 'condition')) + $gradeoptions;
$gradeoptions = array(0 => $strcondnone) + $gradeoptions;
$grouparray = array();
$grouparray[] = $mform->createElement('select', 'conditiongradeitemid', '', $gradeoptions);
@@ -92,6 +94,25 @@ class editsection_form extends moodleform {
'conditiongradeadds', 2, get_string('addgrades', 'condition'), true);
$mform->addHelpButton('conditiongradegroup[0]', 'gradecondition', 'condition');
// Conditions based on user fields
$operators = condition_info::get_condition_user_field_operators();
$useroptions = condition_info::get_condition_user_fields();
asort($useroptions);
$useroptions = array(0 => $strcondnone) + $useroptions;
$grouparray = array();
$grouparray[] =& $mform->createElement('select', 'conditionfield', '', $useroptions);
$grouparray[] =& $mform->createElement('select', 'conditionfieldoperator', '', $operators);
$grouparray[] =& $mform->createElement('text', 'conditionfieldvalue');
$mform->setType('conditionfieldvalue', PARAM_RAW);
$group = $mform->createElement('group', 'conditionfieldgroup', get_string('userfield', 'condition'), $grouparray);
$fieldcount = count($fullcs->conditionsfield) + 1;
$this->repeat_elements(array($group), $fieldcount, array(), 'conditionfieldrepeats', 'conditionfieldadds', 2,
get_string('adduserfields', 'condition'), true);
$mform->addHelpButton('conditionfieldgroup[0]', 'userfield', 'condition');
// Conditions based on completion
$completion = new completion_info($course);
if ($completion->is_enabled()) {
@@ -106,7 +127,7 @@ class editsection_form extends moodleform {
}
}
asort($completionoptions);
$completionoptions = array(0 => get_string('none', 'condition')) +
$completionoptions = array(0 => $strcondnone) +
$completionoptions;
$completionvalues = array(
@@ -145,6 +166,18 @@ class editsection_form extends moodleform {
$num++;
}
$num = 0;
foreach ($fullcs->conditionsfield as $fieldid => $data) {
$groupelements = $mform->getElement(
'conditionfieldgroup[' . $num . ']')->getElements();
$groupelements[0]->setValue($fieldid);
$groupelements[1]->setValue(is_null($data->operator) ? '' :
$data->operator);
$groupelements[2]->setValue(is_null($data->value) ? '' :
$data->value);
$num++;
}
if ($completion->is_enabled()) {
$num = 0;
foreach ($fullcs->conditionscompletion as $othercmid => $state) {
@@ -178,6 +211,25 @@ class editsection_form extends moodleform {
$errors['availablefrom'] = get_string('badavailabledates', 'condition');
}
// Conditions: Verify that the user profile field has not been declared more than once
if (array_key_exists('conditionfieldgroup', $data)) {
// Array to store the existing fields
$arrcurrentfields = array();
// Error message displayed if any condition is declared more than once. We use lang string because
// this way we don't actually generate the string unless there is an error.
$stralreadydeclaredwarning = new lang_string('fielddeclaredmultipletimes', 'condition');
foreach ($data['conditionfieldgroup'] as $i => $fielddata) {
if ($fielddata['conditionfield'] == 0) { // Don't need to bother if none is selected
continue;
}
if (in_array($fielddata['conditionfield'], $arrcurrentfields)) {
$errors["conditionfieldgroup[{$i}]"] = $stralreadydeclaredwarning->out();
}
// Add the field to the array
$arrcurrentfields[] = $fielddata['conditionfield'];
}
}
return $errors;
}
}
+2
View File
@@ -1111,6 +1111,7 @@ function get_array_of_activities($courseid) {
condition_info::fill_availability_conditions($rawmods[$seq]);
$mod[$seq]->conditionscompletion = $rawmods[$seq]->conditionscompletion;
$mod[$seq]->conditionsgrade = $rawmods[$seq]->conditionsgrade;
$mod[$seq]->conditionsfield = $rawmods[$seq]->conditionsfield;
}
$modname = $mod[$seq]->mod;
@@ -2928,6 +2929,7 @@ function delete_course_module($id) {
// very quick on an empty table)
$DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id));
$DB->delete_records('course_modules_availability', array('coursemoduleid'=> $cm->id));
$DB->delete_records('course_modules_avail_fields', array('coursemoduleid' => $cm->id));
$DB->delete_records('course_completion_criteria', array('moduleinstance' => $cm->id,
'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
+51 -2
View File
@@ -259,6 +259,15 @@ abstract class moodleform_mod extends moodleform {
$num++;
}
$num = 0;
foreach($fullcm->conditionsfield as $field => $details) {
$groupelements = $mform->getElement('conditionfieldgroup['.$num.']')->getElements();
$groupelements[0]->setValue($field);
$groupelements[1]->setValue(is_null($details->operator) ? '' : $details->operator);
$groupelements[2]->setValue(is_null($details->value) ? '' : $details->value);
$num++;
}
if ($completion->is_enabled()) {
$num=0;
foreach($fullcm->conditionscompletion as $othercmid=>$state) {
@@ -349,6 +358,25 @@ abstract class moodleform_mod extends moodleform {
}
}
// Conditions: Verify that the user profile field has not been declared more than once
if (array_key_exists('conditionfieldgroup', $data)) {
// Array to store the existing fields
$arrcurrentfields = array();
// Error message displayed if any condition is declared more than once. We use lang string because
// this way we don't actually generate the string unless there is an error.
$stralreadydeclaredwarning = new lang_string('fielddeclaredmultipletimes', 'condition');
foreach ($data['conditionfieldgroup'] as $i => $fielddata) {
if ($fielddata['conditionfield'] == 0) { // Don't need to bother if none is selected
continue;
}
if (in_array($fielddata['conditionfield'], $arrcurrentfields)) {
$errors["conditionfieldgroup[{$i}]"] = $stralreadydeclaredwarning->out();
}
// Add the field to the array
$arrcurrentfields[] = $fielddata['conditionfield'];
}
}
return $errors;
}
@@ -471,6 +499,8 @@ abstract class moodleform_mod extends moodleform {
}
if (!empty($CFG->enableavailability)) {
// String used by conditions
$strnone = get_string('none','condition');
// Conditional availability
// Available from/to defaults to midnight because then the display
@@ -504,7 +534,7 @@ abstract class moodleform_mod extends moodleform {
$gradeoptions[$id] = $item->get_name();
}
asort($gradeoptions);
$gradeoptions = array(0=>get_string('none','condition'))+$gradeoptions;
$gradeoptions = array(0 => $strnone) + $gradeoptions;
$grouparray = array();
$grouparray[] =& $mform->createElement('select','conditiongradeitemid','',$gradeoptions);
@@ -522,14 +552,33 @@ abstract class moodleform_mod extends moodleform {
$ci = new condition_info($this->_cm, CONDITION_MISSING_EXTRATABLE);
$this->_cm = $ci->get_full_course_module();
$count = count($this->_cm->conditionsgrade)+1;
$fieldcount = count($this->_cm->conditionsfield) + 1;
} else {
$count = 1;
$fieldcount = 1;
}
$this->repeat_elements(array($group), $count, array(), 'conditiongraderepeats', 'conditiongradeadds', 2,
get_string('addgrades', 'condition'), true);
$mform->addHelpButton('conditiongradegroup[0]', 'gradecondition', 'condition');
// Conditions based on user fields
$operators = condition_info::get_condition_user_field_operators();
$useroptions = condition_info::get_condition_user_fields();
asort($useroptions);
$useroptions = array(0 => $strnone) + $useroptions;
$grouparray = array();
$grouparray[] =& $mform->createElement('select', 'conditionfield', '', $useroptions);
$grouparray[] =& $mform->createElement('select', 'conditionfieldoperator', '', $operators);
$grouparray[] =& $mform->createElement('text', 'conditionfieldvalue');
$mform->setType('conditionfieldvalue', PARAM_RAW);
$group = $mform->createElement('group', 'conditionfieldgroup', get_string('userfield', 'condition'), $grouparray);
$this->repeat_elements(array($group), $fieldcount, array(), 'conditionfieldrepeats', 'conditionfieldadds', 2,
get_string('adduserfields', 'condition'), true);
$mform->addHelpButton('conditionfieldgroup[0]', 'userfield', 'condition');
// Conditions based on completion
$completion = new completion_info($COURSE);
if ($completion->is_enabled()) {
@@ -544,7 +593,7 @@ abstract class moodleform_mod extends moodleform {
}
}
asort($completionoptions);
$completionoptions = array(0=>get_string('none','condition'))+$completionoptions;
$completionoptions = array(0 => $strnone) + $completionoptions;
$completionvalues=array(
COMPLETION_COMPLETE=>get_string('completion_complete','condition'),
+5
View File
@@ -25,6 +25,7 @@
$string['addcompletions'] = 'Add {no} activity conditions to form';
$string['addgrades'] = 'Add {no} grade conditions to form';
$string['adduserfields'] = 'Add {no} user field conditions to form';
$string['availabilityconditions'] = 'Restrict access';
$string['availablefrom'] = 'Allow access from';
$string['availablefrom_help'] = 'Access from/to dates determine when students can access the activity via a link on the course page.
@@ -47,6 +48,7 @@ $string['completion_incomplete'] = 'must not be marked complete';
$string['completion_pass'] = 'must be complete with pass grade';
$string['configenableavailability'] = 'When enabled, this lets you set conditions (based on date, grade, or completion) that control whether an activity or resource can be accessed.';
$string['enableavailability'] = 'Enable conditional access';
$string['fielddeclaredmultipletimes'] = 'You can not declare the same field more than once per activity.';
$string['grade_atleast'] = 'must be at least';
$string['gradecondition'] = 'Grade condition';
$string['gradecondition_help'] = 'This setting determines any grade conditions which must be met in order to access the activity.
@@ -74,12 +76,15 @@ $string['requires_grade_any'] = 'Not available until you have a grade in <strong
$string['requires_grade_max'] = 'Not available unless you get an appropriate score in <strong>{$a}</strong>.';
$string['requires_grade_min'] = 'Not available until you achieve a required score in <strong>{$a}</strong>.';
$string['requires_grade_range'] = 'Not available unless you get a particular score in <strong>{$a}</strong>.';
$string['requires_user_field_restriction'] = 'Not available unless your <strong>{$a->field}</strong> {$a->operator} <strong>{$a->value}</strong>.';
$string['showavailability'] = 'Before activity can be accessed';
$string['showavailabilitysection'] = 'Before section can be accessed';
$string['showavailability_hide'] = 'Hide activity entirely';
$string['showavailability_show'] = 'Show activity greyed-out, with restriction information';
$string['showavailabilitysection_hide'] = 'Hide section entirely';
$string['showavailabilitysection_show'] = 'Show section greyed-out, with restriction information';
$string['userfield'] = 'User field';
$string['userfield_help'] = 'You can restrict access based on any field from the user&rsquo;s profile.';
$string['userrestriction_hidden'] = 'Restricted (completely hidden, no message): &lsquo;{$a}&rsquo;';
$string['userrestriction_visible'] = 'Restricted: &lsquo;{$a}&rsquo;';
$string['groupingnoaccess'] = 'You do not currently belong to a group which has access to this section. ';
+327 -3
View File
@@ -50,6 +50,36 @@ define('CONDITION_MISSING_EXTRATABLE', 1);
*/
define('CONDITION_MISSING_EVERYTHING', 2);
/**
* OP_CONTAINS - comparison operator that determines whether a specified user field contains
* a provided variable
*/
define('OP_CONTAINS', 'contains');
/**
* OP_DOES_NOT_CONTAIN - comparison operator that determines whether a specified user field does not
* contain a provided variable
*/
define('OP_DOES_NOT_CONTAIN', 'doesnotcontain');
/**
* OP_IS_EQUAL_TO - comparison operator that determines whether a specified user field is equal to
* a provided variable
*/
define('OP_IS_EQUAL_TO', 'isequalto');
/**
* OP_STARTS_WITH - comparison operator that determines whether a specified user field starts with
* a provided variable
*/
define('OP_STARTS_WITH', 'startswith');
/**
* OP_ENDS_WITH - comparison operator that determines whether a specified user field ends with
* a provided variable
*/
define('OP_ENDS_WITH', 'endswith');
/**
* OP_IS_EMPTY - comparison operator that determines whether a specified user field is empty
*/
define('OP_IS_EMPTY', 'isempty');
require_once($CFG->libdir.'/completionlib.php');
/**
@@ -334,7 +364,7 @@ class condition_info_section extends condition_info_base {
*/
abstract class condition_info_base {
/** @var object, bool, string, string, array */
protected $item, $gotdata, $availtable, $idfieldname, $usergroupings;
protected $item, $gotdata, $availtable, $availfieldtable, $idfieldname, $usergroupings;
/**
* Constructs with item details.
@@ -373,6 +403,9 @@ abstract class condition_info_base {
// DB table to store availability conditions
$this->availtable = $tableprefix . '_availability';
// DB table to store availability conditions for user fields
$this->availfieldtable = $tableprefix . '_avail_fields';
// Name of module/section ID field in DB
$this->idfieldname = $idfield;
@@ -408,7 +441,8 @@ abstract class condition_info_base {
$this->gotdata = true;
// Missing extra data
if (!isset($item->conditionsgrade) || !isset($item->conditionscompletion)) {
if (!isset($item->conditionsgrade) || !isset($item->conditionscompletion)
|| !isset($item->conditionsfield)) {
if ($expectingmissing<CONDITION_MISSING_EXTRATABLE) {
debugging('Performance warning: condition_info constructor is ' .
'faster if you pass in a $item from get_fast_modinfo or ' .
@@ -446,9 +480,11 @@ abstract class condition_info_base {
}
// Does nothing if the variables are already present
if (!isset($item->conditionsgrade) || !isset($item->conditionscompletion)) {
if (!isset($item->conditionsgrade) || !isset($item->conditionscompletion)
|| !isset($item->conditionsfield)) {
$item->conditionsgrade = array();
$item->conditionscompletion = array();
$item->conditionsfield = array();
$conditions = $DB->get_records_sql('
SELECT
@@ -470,6 +506,38 @@ abstract class condition_info_base {
$item->conditionsgrade[$condition->gradeitemid] = $minmax;
}
}
// For user fields
$sql = "SELECT a.id as cmaid, a.*, uf.*
FROM {" . $tableprefix . "_avail_fields} a
LEFT JOIN {user_info_field} uf
ON a.customfieldid = uf.id
WHERE " . $idfield . " = :itemid";
if ($conditions = $DB->get_records_sql($sql, array('itemid' => $item->id))) {
foreach ($conditions as $condition) {
// If the custom field is not empty, then
// we have a custom profile field
if (!empty($condition->customfieldid)) {
$field = $condition->customfieldid;
// Check if the profile field name is not empty, if it is
// then the custom profile field no longer exists, so
// display !missing instead.
if (!empty($condition->name)) {
$fieldname = $condition->name;
} else {
$fieldname = '!missing';
}
} else {
$field = $condition->userfield;
$fieldname = $condition->userfield;
}
$details = new stdClass;
$details->fieldname = $fieldname;
$details->operator = $condition->operator;
$details->value = $condition->value;
$item->conditionsfield[$field] = $details;
}
}
}
}
@@ -503,6 +571,63 @@ abstract class condition_info_base {
return $this->item;
}
/**
* The operators that provide the relationship
* between a field and a value.
*
* @return array Associative array from operator constant to display name
*/
public static function get_condition_user_field_operators() {
return array(
OP_CONTAINS => get_string('contains', 'filters'),
OP_DOES_NOT_CONTAIN => get_string('doesnotcontain', 'filters'),
OP_IS_EQUAL_TO => get_string('isequalto', 'filters'),
OP_STARTS_WITH => get_string('startswith', 'filters'),
OP_ENDS_WITH => get_string('endswith', 'filters'),
OP_IS_EMPTY => get_string('isempty', 'filters')
);
}
/**
* The user fields we can compare
*
* @global $DB
* @return array Associative array from user field constants to display name
*/
public static function get_condition_user_fields() {
global $DB;
$userfields = array(
'firstname' => get_user_field_name('firstname'),
'lastname' => get_user_field_name('lastname'),
'email' => get_user_field_name('email'),
'city' => get_user_field_name('city'),
'country' => get_user_field_name('country'),
'interests' => get_user_field_name('interests'),
'url' => get_user_field_name('url'),
'icq' => get_user_field_name('icq'),
'skype' => get_user_field_name('skype'),
'aim' => get_user_field_name('aim'),
'yahoo' => get_user_field_name('yahoo'),
'msn' => get_user_field_name('msn'),
'idnumber' => get_user_field_name('idnumber'),
'institution' => get_user_field_name('institution'),
'department' => get_user_field_name('department'),
'phone1' => get_user_field_name('phone1'),
'phone2' => get_user_field_name('phone2'),
'address' => get_user_field_name('address')
);
// Go through the custom profile fields now
if ($user_info_fields = $DB->get_records('user_info_field')) {
foreach ($user_info_fields as $field) {
$userfields[$field->id] = $field->name;
}
}
return $userfields;
}
/**
* Adds to the database a condition based on completion of another module.
*
@@ -522,6 +647,39 @@ abstract class condition_info_base {
$this->item->conditionscompletion[$cmid] = $requiredcompletion;
}
/**
* Adds user fields condition
*
* @param mixed $field numeric if it is a user profile field, character
* if it is a column in the user table
* @param int $operator specifies the relationship between field and value
* @param char $value the value of the field
*/
public function add_user_field_condition($field, $operator, $value) {
global $DB;
// Get the field name
$idfieldname = $this->idfieldname;
$objavailfield = new stdClass;
$objavailfield->$idfieldname = $this->item->id;
if (is_numeric($field)) { // If the condition field is numeric then it is a custom profile field
// Need to get the field name so we can add it to the cache
$ufield = $DB->get_field('user_info_field', 'name', array('id' => $field));
$objavailfield->fieldname = $ufield;
$objavailfield->customfieldid = $field;
} else {
$objavailfield->fieldname = $field;
$objavailfield->userfield = $field;
}
$objavailfield->operator = $operator;
$objavailfield->value = $value;
$DB->insert_record($this->availfieldtable, $objavailfield, false);
// Store in memory too
$this->item->conditionsfield[$field] = $objavailfield;
}
/**
* Adds to the database a condition based on the value of a grade item.
*
@@ -565,11 +723,14 @@ abstract class condition_info_base {
public function wipe_conditions() {
// Wipe from DB
global $DB;
$DB->delete_records($this->availtable, array($this->idfieldname => $this->item->id));
$DB->delete_records($this->availfieldtable, array($this->idfieldname => $this->item->id));
// And from memory
$this->item->conditionsgrade = array();
$this->item->conditionscompletion = array();
$this->item->conditionsfield = array();
}
/**
@@ -630,6 +791,18 @@ abstract class condition_info_base {
}
}
// User field conditions
if (count($this->item->conditionsfield) > 0) {
// Need the array of operators
foreach ($this->item->conditionsfield as $field => $details) {
$a = new stdclass;
$a->field = $details->fieldname;
$a->operator = get_string($details->operator, 'filters');
$a->value = $details->value;
$information .= get_string('requires_user_field_restriction', 'condition', $a) . ' ';
}
}
// The date logic is complicated. The intention of this logic is:
// 1) display date without time where possible (whenever the date is
// midnight)
@@ -822,6 +995,22 @@ abstract class condition_info_base {
}
}
// Check if user field condition
if (count($this->item->conditionsfield) > 0) {
foreach ($this->item->conditionsfield as $field => $details) {
$uservalue = $this->get_cached_user_profile_field($userid, $field, $grabthelot);
if (!$fieldconditionmet = $this->is_field_condition_met($details->operator, $uservalue, $details->value)) {
// Set available to false
$available = false;
$a = new stdClass();
$a->field = $details->fieldname;
$a->operator = get_string($details->operator, 'filters');
$a->value = $details->value;
$information .= get_string('requires_user_field_restriction', 'condition', $a) . ' ';
}
}
}
// Test dates
if ($this->item->availablefrom) {
if (time() < $this->item->availablefrom) {
@@ -983,6 +1172,132 @@ abstract class condition_info_base {
}
}
/**
* Returns true if a field meets the required conditions, false otherwise.
*
* @param char $operator the requirement/condition
* @param char $uservalue the user's value
* @param char $value the value required
* @return boolean
*/
private function is_field_condition_met($operator, $uservalue, $value) {
$fieldconditionmet = true;
switch($operator) {
case OP_CONTAINS: // contains
$pos = strpos($uservalue, $value);
if ($pos === false) {
$fieldconditionmet = false;
}
break;
case OP_DOES_NOT_CONTAIN: // does not contain
if (!empty($value)) {
$pos = strpos($uservalue, $value);
if ($pos !== false) {
$fieldconditionmet = false;
}
}
break;
case OP_IS_EQUAL_TO: // equal to
if ($value !== $uservalue) {
$fieldconditionmet = false;
}
break;
case OP_STARTS_WITH: // starts with
$length = strlen($value);
if ((substr($uservalue, 0, $length) !== $value)) {
$fieldconditionmet = false;
}
break;
case OP_ENDS_WITH: // ends with
$length = strlen($value);
$start = $length * -1; // negative
if (substr($uservalue, $start) !== $value) {
$fieldconditionmet = false;
}
break;
case OP_IS_EMPTY: // is empty
if (!empty($uservalue)) {
$fieldconditionmet = false;
}
break;
}
return $fieldconditionmet;
}
/**
* Return the value for a user's profile field
*
* @param int $userid set if requesting grade for a different user (does
* not use cache)
* @param int $fieldid the user profile field id
* @param bool $grabthelot If true, grabs all the user profile fields for
* current user on this course, so that later ones come from cache
* @return string the user value, or false if user does not have a user field value yet
*/
private function get_cached_user_profile_field($userid, $fieldid, $grabthelot) {
global $USER, $DB, $SESSION;
// Check if the field is a custom profile field
$iscustomprofilefield = is_numeric($fieldid) ? true : false;
if ($userid == 0 || $userid == $USER->id) {
if ($iscustomprofilefield) {
// For current user, go via cache in session
if (empty($SESSION->userfieldcache) || $SESSION->userfieldcacheuserid != $USER->id) {
$SESSION->userfieldcache = array();
$SESSION->userfieldcacheuserid = $USER->id;
}
if (!array_key_exists($fieldid, $SESSION->userfieldcache)) {
if ($grabthelot) {
// Get all custom profile field values for user
$sql = "SELECT uf.id, ud.data
FROM {user_info_field} uf
LEFT JOIN {user_info_data} ud ON uf.id = ud.fieldid
WHERE ud.userid = :userid";
if ($records = $DB->get_records_sql($sql, array('userid' => $USER->id))) {
foreach ($records as $r) {
$SESSION->userfieldcache[$r->id] = $r->data;
}
}
} else {
// Just get specified user field
$sql = "SELECT ud.data
FROM {user_info_data} ud
INNER JOIN {user_info_field} uf ON ud.fieldid = uf.id
WHERE uf.id = :fieldid
AND ud.userid = :userid";
if ($record = $DB->get_record_sql($sql, array('fieldid' => $fieldid, 'userid' => $USER->id))) {
$field = $record->data;
} else {
$field = false;
}
$SESSION->userfieldcache[$fieldid] = $field;
}
}
if (!empty($SESSION->userfieldcache[$fieldid])) {
return $SESSION->userfieldcache[$fieldid];
} else {
return false;
}
} else {
return $USER->$fieldid;
}
} else {
if ($iscustomprofilefield) {
$sql = "SELECT ud.data
FROM {user_info_data} ud
INNER JOIN {user_info_field} uf ON ud.fieldid = uf.id
WHERE uf.id = :fieldid
AND ud.userid = :userid";
if ($record = $DB->get_record_sql($sql, array('fieldid' => $fieldid, 'userid' => $userid))) {
return $record->data;
}
} else {
return $DB->get_field('user', $fieldid, array('id' => $userid), MUST_EXIST);
}
// If it reaches here, then no matches found
return false;
}
}
/**
* For testing only. Wipes information cached in user session.
*
@@ -992,6 +1307,8 @@ abstract class condition_info_base {
global $SESSION;
unset($SESSION->gradescorecache);
unset($SESSION->gradescorecacheuserid);
unset($SESSION->userfieldcache);
unset($SESSION->userfieldcacheuserid);
}
/**
@@ -1023,6 +1340,13 @@ abstract class condition_info_base {
unformat_float($record['conditiongrademin']), unformat_float($record['conditiongrademax']));
}
}
foreach ($fromform->conditionfieldgroup as $record) {
if($record['conditionfield']) {
$ci->add_user_field_condition($record['conditionfield'],
$record['conditionfieldoperator'],
$record['conditionfieldvalue']);
}
}
if(isset ($fromform->conditioncompletiongroup)) {
foreach($fromform->conditioncompletiongroup as $record) {
if($record['conditionsourcecmid']) {
+33 -5
View File
@@ -330,7 +330,7 @@
<INDEX NAME="idnumber-course" UNIQUE="false" FIELDS="idnumber, course" COMMENT="non unique index (although programatically we are guarantying some sort of uniqueness both under this table and the grade_items one). TODO: We need a central store of module idnumbers in the future." PREVIOUS="instance"/>
</INDEXES>
</TABLE>
<TABLE NAME="course_modules_availability" COMMENT="Table stores conditions that affect whether a module/activity is currently available to students or not." PREVIOUS="course_modules" NEXT="course_modules_completion">
<TABLE NAME="course_modules_availability" COMMENT="Table stores conditions that affect whether a module/activity is currently available to students or not." PREVIOUS="course_modules" NEXT="course_modules_avail_fields">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="coursemoduleid"/>
<FIELD NAME="coursemoduleid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the module whose availability is being restricted by this condition." PREVIOUS="id" NEXT="sourcecmid"/>
@@ -347,7 +347,21 @@
<KEY NAME="gradeitemid" TYPE="foreign" FIELDS="gradeitemid" REFTABLE="grade_items" REFFIELDS="id" PREVIOUS="sourcecmid"/>
</KEYS>
</TABLE>
<TABLE NAME="course_modules_completion" COMMENT="Stores the completion state (completed or not completed, etc) of each user on each activity." PREVIOUS="course_modules_availability" NEXT="course_sections">
<TABLE NAME="course_modules_avail_fields" COMMENT="Stores user field conditions that affect whether an activity is currently available to students." PREVIOUS="course_modules_availability" NEXT="course_modules_completion">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="coursemoduleid"/>
<FIELD NAME="coursemoduleid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the module whose availability is being restricted by this condition." PREVIOUS="id" NEXT="userfield"/>
<FIELD NAME="userfield" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false" COMMENT="The user profile field this record relates to if it is not a custom profile field" PREVIOUS="coursemoduleid" NEXT="customfieldid"/>
<FIELD NAME="customfieldid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="ID for the custom field if this relates to one" PREVIOUS="userfield" NEXT="operator"/>
<FIELD NAME="operator" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" COMMENT="The operator, such as less than or equal to, between the field and the value" PREVIOUS="customfieldid" NEXT="value"/>
<FIELD NAME="value" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The required value of the field" PREVIOUS="operator"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="coursemoduleid"/>
<KEY NAME="coursemoduleid" TYPE="foreign" FIELDS="coursemoduleid" REFTABLE="course_modules" REFFIELDS="id" PREVIOUS="primary"/>
</KEYS>
</TABLE>
<TABLE NAME="course_modules_completion" COMMENT="Stores the completion state (completed or not completed, etc) of each user on each activity." PREVIOUS="course_modules_avail_fields" NEXT="course_sections">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="coursemoduleid"/>
<FIELD NAME="coursemoduleid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Activity that has been completed (or not)." PREVIOUS="id" NEXT="userid"/>
@@ -386,7 +400,7 @@
<INDEX NAME="course_section" UNIQUE="true" FIELDS="course, section"/>
</INDEXES>
</TABLE>
<TABLE NAME="course_sections_availability" COMMENT="Completion or grade conditions that affect if a section is currently available to students." PREVIOUS="course_sections" NEXT="course_request">
<TABLE NAME="course_sections_availability" COMMENT="Completion or grade conditions that affect if a section is currently available to students." PREVIOUS="course_sections" NEXT="course_sections_avail_fields">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="coursesectionid"/>
<FIELD NAME="coursesectionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the section whose availability is being restricted by this condition." PREVIOUS="id" NEXT="sourcecmid"/>
@@ -403,7 +417,21 @@
<KEY NAME="gradeitemid" TYPE="foreign" FIELDS="gradeitemid" REFTABLE="grade_items" REFFIELDS="id" PREVIOUS="sourcecmid"/>
</KEYS>
</TABLE>
<TABLE NAME="course_request" COMMENT="course requests" PREVIOUS="course_sections_availability" NEXT="filter_active">
<TABLE NAME="course_sections_avail_fields" COMMENT="Stores user field conditions that affect whether an activity is currently available to students." PREVIOUS="course_sections_availability" NEXT="course_request">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="coursesectionid"/>
<FIELD NAME="coursesectionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the section whose availability is being restricted by this condition." PREVIOUS="id" NEXT="userfield"/>
<FIELD NAME="userfield" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false" COMMENT="The user profile field this record relates to if it is not a custom profile field" PREVIOUS="coursesectionid" NEXT="customfieldid"/>
<FIELD NAME="customfieldid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="ID for the custom field if this relates to one" PREVIOUS="userfield" NEXT="operator"/>
<FIELD NAME="operator" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" COMMENT="The operator, such as less than or equal to, between the field and the value" PREVIOUS="customfieldid" NEXT="value"/>
<FIELD NAME="value" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The required value of the field" PREVIOUS="operator"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="coursesectionid"/>
<KEY NAME="coursesectionid" TYPE="foreign" FIELDS="coursesectionid" REFTABLE="course_sections" REFFIELDS="id" PREVIOUS="primary"/>
</KEYS>
</TABLE>
<TABLE NAME="course_request" COMMENT="course requests" PREVIOUS="course_sections_avail_fields" NEXT="filter_active">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="fullname"/>
<FIELD NAME="fullname" TYPE="char" LENGTH="254" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="shortname"/>
@@ -2847,4 +2875,4 @@
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
</XMLDB>
+45
View File
@@ -594,6 +594,51 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2012051100.03);
}
if ($oldversion < 2012051700.02) {
// Define table course_modules_avail_fields to be created
$table = new xmldb_table('course_modules_avail_fields');
// Adding fields to table course_modules_avail_fields
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('coursemoduleid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('userfield', XMLDB_TYPE_CHAR, '50', null, null, null, null);
$table->add_field('customfieldid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
$table->add_field('operator', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
$table->add_field('value', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
// Adding keys to table course_modules_avail_fields
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('coursemoduleid', XMLDB_KEY_FOREIGN, array('coursemoduleid'), 'course_modules', array('id'));
// Conditionally launch create table for course_modules_avail_fields
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Define table section_modules_avail_fields to be created
$table = new xmldb_table('course_sections_avail_fields');
// Adding fields to table course_modules_section_fields
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('coursesectionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('userfield', XMLDB_TYPE_CHAR, '50', null, null, null, null);
$table->add_field('customfieldid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
$table->add_field('operator', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
$table->add_field('value', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
// Adding keys to table course_modules_avail_fields
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('coursesectionid', XMLDB_KEY_FOREIGN, array('coursesectionid'), 'course_sections', array('id'));
// Conditionally launch create table for section_modules_avail_fields
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Main savepoint reached
upgrade_main_savepoint(true, 2012051700.02);
}
if ($oldversion < 2012052100.00) {
// Define field referencefileid to be added to files.
+10
View File
@@ -604,6 +604,12 @@ class cm_info extends stdClass {
*/
public $conditionsgrade;
/**
* Availability conditions for this course-module based on user fields
* @var array
*/
public $conditionsfield;
/**
* Plural name of module type, e.g. 'Forums' - from lang file
* @deprecated Do not use this value (you can obtain it by calling get_string instead); it
@@ -1004,6 +1010,8 @@ class cm_info extends stdClass {
? $mod->conditionscompletion : array();
$this->conditionsgrade = isset($mod->conditionsgrade)
? $mod->conditionsgrade : array();
$this->conditionsfield = isset($mod->conditionsfield)
? $mod->conditionsfield : array();
// Get module plural name.
// TODO This was a very old performance hack and should now be removed as the information
@@ -1503,6 +1511,8 @@ class section_info extends stdClass {
? $data->conditionscompletion : array();
$this->conditionsgrade = isset($data->conditionsgrade)
? $data->conditionsgrade : array();
$this->conditionsfield = isset($data->conditionsfield)
? $data->conditionsfield : array();
// Other data from other places
$this->course = $courseid;
+21 -1
View File
@@ -3581,6 +3581,12 @@ function get_user_field_name($field) {
// Some fields have language strings which are not the same as field name
switch ($field) {
case 'phone1' : return get_string('phone');
case 'url' : return get_string('webpage');
case 'icq' : return get_string('icqnumber');
case 'skype' : return get_string('skypeid');
case 'aim' : return get_string('aimid');
case 'yahoo' : return get_string('yahooid');
case 'msn' : return get_string('msnid');
}
// Otherwise just use the same lang string
return get_string($field);
@@ -4509,6 +4515,15 @@ function remove_course_contents($courseid, $showfeedback = true, array $options
echo $OUTPUT->notification($strdeleted.get_string('completion', 'completion'), 'notifysuccess');
}
// Remove all data from availability and completion tables that is associated
// with course-modules belonging to this course. Note this is done even if the
// features are not enabled now, in case they were enabled previously
$subquery = 'coursemoduleid IN (SELECT id from {course_modules} WHERE course = ?)';
$subqueryparam = array($courseid);
$DB->delete_records_select('course_modules_completion', $subquery, $subqueryparam);
$DB->delete_records_select('course_modules_availability', $subquery, $subqueryparam);
$DB->delete_records_select('course_modules_avail_fields', $subquery, $subqueryparam);
// Remove all data from gradebook - this needs to be done before course modules
// because while deleting this information, the system may need to reference
// the course modules that own the grades.
@@ -4584,6 +4599,9 @@ function remove_course_contents($courseid, $showfeedback = true, array $options
$DB->delete_records_select('course_modules_availability',
'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)',
array($courseid));
$DB->delete_records_select('course_modules_avail_fields',
'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)',
array($courseid));
// Remove course-module data.
$cms = $DB->get_records('course_modules', array('course'=>$course->id));
@@ -4598,7 +4616,6 @@ function remove_course_contents($courseid, $showfeedback = true, array $options
context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
$DB->delete_records('course_modules', array('id'=>$cm->id));
}
if ($showfeedback) {
echo $OUTPUT->notification($strdeleted.get_string('type_mod_plural', 'plugin'), 'notifysuccess');
}
@@ -4694,6 +4711,9 @@ function remove_course_contents($courseid, $showfeedback = true, array $options
$DB->delete_records_select('course_sections_availability',
'coursesectionid IN (SELECT id from {course_sections} WHERE course=?)',
array($course->id));
$DB->delete_records_select('course_sections_avail_fields',
'coursesectionid IN (SELECT id from {course_sections} WHERE course=?)',
array($course->id));
$DB->delete_records('course_sections', array('course'=>$course->id));
// delete legacy, section and any other course files
+7
View File
@@ -289,6 +289,13 @@ function profile_delete_field($id) {
print_error('cannotdeletecustomfield');
}
// Delete any module dependencies for this field
$DB->delete_records('course_modules_avail_fields', array('customfieldid' => $id));
$DB->delete_records('course_sections_avail_fields', array('customfieldid' => $id));
// Need to rebuild course cache to update the info
rebuild_course_cache();
/// Try to remove the record from the database
$DB->delete_records('user_info_field', array('id'=>$id));