diff --git a/classes/external.php b/classes/external.php
index 4396877..0395ec8 100755
--- a/classes/external.php
+++ b/classes/external.php
@@ -291,9 +291,23 @@ public static function submit_choicegroup_response($choicegroupid, $data) {
}
if (!choicegroup_get_user_answer($choicegroup, $USER) || $choicegroup->allowupdate) {
+ // Comparable to view.php.
+ if ($choicegroup->restrictbygrouping) {
+ $usergroupassigments = choicegroup_get_all_relevant_group_assignments($choicegroup->id);
+ $groupgrouping = choicegroup_structure_groupings_to_group($choicegroup->option);
+ }
if ($choicegroup->multipleenrollmentspossible) {
foreach($choicegroup->option as $optionid => $text) {
if (in_array($optionid, $responses)) {
+ if ($choicegroup->restrictbygrouping && !(empty($usergroupassigments))) {
+ $choicerecord = $DB->get_record('choicegroup_options', array('id' => $optionid),
+ '*', MUST_EXIST);
+ $groupavailable = choicegroup_check_group_available($choicerecord->groupid,
+ $usergroupassigments, $groupgrouping);
+ if (!$groupavailable) {
+ throw new moodle_exception('restrictgroupingsconflict', 'webservice');
+ }
+ }
choicegroup_user_submit_response($optionid, $choicegroup, $USER->id, $course, $cm);
} else {
// Remove group selection if selected.
@@ -315,6 +329,15 @@ public static function submit_choicegroup_response($choicegroupid, $data) {
} else { // !multipleenrollmentspossible
if (count($responses) == 1) {
$responses = reset($responses);
+ if ($choicegroup->restrictbygrouping && !(empty($usergroupassigments))) {
+ $choicerecord = $DB->get_record('choicegroup_options', array('id' => $responses->id),
+ '*', MUST_EXIST);
+ $groupavailable = choicegroup_check_group_available($choicerecord->groupid,
+ $usergroupassigments, $groupgrouping);
+ if (!$groupavailable) {
+ throw new moodle_exception('restrictgroupingsconflict', 'webservice');
+ }
+ }
choicegroup_user_submit_response($responses, $choicegroup, $USER->id, $course, $cm);
}
}
diff --git a/db/install.xml b/db/install.xml
index d023fa2..7a257e8 100644
--- a/db/install.xml
+++ b/db/install.xml
@@ -18,8 +18,10 @@
-
-
+
+
+
+
@@ -46,5 +48,17 @@
+
diff --git a/db/upgrade.php b/db/upgrade.php
index af0afde..62dfb35 100644
--- a/db/upgrade.php
+++ b/db/upgrade.php
@@ -48,7 +48,7 @@ function xmldb_choicegroup_upgrade($oldversion) {
// Adding fields to table choicegroup
$newField = $table->add_field('multipleenrollmentspossible', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
- $dbman->add_field($table, $newField);
+ $dbman->add_field($table, $newField);
upgrade_mod_savepoint(true, 2013070900, 'choicegroup');
@@ -96,6 +96,39 @@ function xmldb_choicegroup_upgrade($oldversion) {
// Group choice savepoint reached.
upgrade_mod_savepoint(true, 2021080500, 'choicegroup');
}
+ if ($oldversion < 2023011600) {
+
+ // Define field restrictbygrouping to be added to choicegroup.
+ $table = new xmldb_table('choicegroup');
+ $field = new xmldb_field('restrictbygrouping', XMLDB_TYPE_INTEGER, '2', null, null, null, '0', 'onlyactive');
+
+ // Conditionally launch add field restrictbygrouping.
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+ // Define field restrictchoicesbehaviour to be added to choicegroup.
+ $field = new xmldb_field('restrictchoicesbehaviour', XMLDB_TYPE_INTEGER, '2', null, null, null, '0', 'restrictbygrouping');
+
+ // Conditionally launch add field restrictchoicesbehaviour.
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+ // Choicegroup savepoint reached.
+
+ // Create Table for groupings.
+ // Define field id to be added to choicegroup_groupings.
+ $table = new xmldb_table('choicegroup_groupings');
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null);
+ $table->add_field('choicegroupid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
+ $table->add_field('groupingid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'choicegroupid');
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
+ // Conditionally create the table.
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+ upgrade_mod_savepoint(true, 2023011600, 'choicegroup');
+ }
return true;
}
diff --git a/lang/en/choicegroup.php b/lang/en/choicegroup.php
index e07219d..f4e10fc 100644
--- a/lang/en/choicegroup.php
+++ b/lang/en/choicegroup.php
@@ -172,3 +172,23 @@
$string['maxenrollments'] = 'Max. enrollments';
$string['maxenrollments_help'] = 'This option allows to limit the number of group enrollments for a participant. Use default value **0** if there is no limit.';
+$string['groupingrestrict'] = 'Restrict choices based on grouping assignments';
+$string['selectedgroupings'] = 'Restrict by grouping?';
+$string['selectedgroupings_help'] = 'If enabled, participants can only choose groups which do not belong to a grouping they are already assigned to (manually or by a group choice activity).';
+$string['assignedtogrouping'] = '(You are already assigned to a group which contradicts this choice)';
+$string['submitfailedgroupingrestriction'] = 'You are already assigned to a group which contradicts this choice.';
+$string['limitationbehaviour'] = 'Limitation behaviour';
+$string['restrictchoicesbehaviour'] = 'Choose limitation behaviour?';
+$string['restrictchoicesbehaviour_help'] = '"Hide group from group list" - will hide all groups which can not be selected due to previous assignments
+ "Show group as dimmed" - will show the choice but choice can not be selected
+ "Show group with limitation notice" - inform user about conflicting groups ';
+$string['hidegroup'] = 'Hide group from group list';
+$string['dimmgroup'] = 'Show group as dimmed';
+$string['informlimit'] = 'Show group with limitation notice';
+$string['informlimitgroup'] = 'Show group with information on conflicting groups';
+$string['reasongrouplimitationplural'] = '(You can not select this group as you are already assigned to the groups: {$a} )';
+$string['reasongrouplimitationsingular'] = '(You can not select this group as you are already assigned to the group: {$a} )';
+$string['reasongrouplimitationempty'] = '(You can not select this group, however no conflicting group can be found - this might be an error in the programm code)';
+$string['hidechoicetostudents'] = '(If a student had the same group assigment, this choice would not be shown)';
+$string['conflictinggroupassignment'] = 'Your Group assigment are conflicting. Therefore you can not participate in this groupchoice activity.';
+
diff --git a/lib.php b/lib.php
index 4bbf52f..1316ea0 100644
--- a/lib.php
+++ b/lib.php
@@ -51,6 +51,10 @@
define('CHOICEGROUP_SORTGROUPS_CREATEDATE', '1');
define('CHOICEGROUP_SORTGROUPS_NAME', '2');
+define('CHOICEGROUP_RESTRICTGROUPINGS_NOTAVAILABLE', '0');
+define('CHOICEGROUP_RESTRICTGROUPINGS_AVAILABLE', '1');
+define('CHOICEGROUP_RESTRICTGROUPINGS_CONFLICT', '2');
+
// Ugly hack to make 3.11 and 4.0 work seamlessly.
if (!defined('FEATURE_MOD_PURPOSE')) {
define('FEATURE_MOD_PURPOSE', 'mod_purpose');
@@ -197,6 +201,11 @@ function choicegroup_add_instance($choicegroup) {
}
//insert answers
+ if (isset($choicegroup->restrictbygrouping) && $choicegroup->restrictbygrouping) {
+ $choicegroup->restrictbygrouping = $choicegroup->restrictbygrouping;
+ } else {
+ $choicegroup->restrictbygrouping = false;
+ }
$choicegroup->id = $DB->insert_record("choicegroup", $choicegroup);
// deserialize the selected groups
@@ -216,7 +225,18 @@ function choicegroup_add_instance($choicegroup) {
}
$option->timemodified = time();
$DB->insert_record("choicegroup_options", $option);
- }
+ }
+ }
+ if (property_exists($choicegroup, 'restrictbygrouping') && $choicegroup->restrictbygrouping) {
+ if (!empty($choicegroup->selectedgroupings)) {
+ $DB->delete_records("choicegroup_groupings", array('choicegroupid' => $choicegroup->id));
+ foreach ($choicegroup->selectedgroupings as $selectedgrouping) {
+ $groupchoiceassignment = new stdClass();
+ $groupchoiceassignment->choicegroupid = $choicegroup->id;
+ $groupchoiceassignment->groupingid = $selectedgrouping;
+ $DB->insert_record("choicegroup_groupings", $groupchoiceassignment);
+ }
+ }
}
if (class_exists('\core_completion\api')) {
@@ -288,20 +308,34 @@ function choicegroup_update_instance($choicegroup) {
continue 2; // continue the big loop
}
}
- $DB->insert_record("choicegroup_options", $option);
+
+ $DB->insert_record("choicegroup_options", $option);
}
-
}
// remove all remaining pre-existing groups which did not appear in the form (and are thus assumed to have been deleted)
foreach ($preExistingGroups as $preExistingGroup) {
$DB->delete_records("choicegroup_options", array("id"=>$preExistingGroup->id));
}
-
+ if (property_exists($choicegroup, 'restrictbygrouping') && $choicegroup->restrictbygrouping) {
+ if (!empty($choicegroup->selectedgroupings)) {
+ $DB->delete_records("choicegroup_groupings", array('choicegroupid' => $choicegroup->id));
+ foreach ($choicegroup->selectedgroupings as $selectedgrouping) {
+ $groupchoiceassignment = new stdClass();
+ $groupchoiceassignment->choicegroupid = $choicegroup->id;
+ $groupchoiceassignment->groupingid = $selectedgrouping;
+ $DB->insert_record("choicegroup_groupings", $groupchoiceassignment);
+ }
+ }
+ }
if (class_exists('\core_completion\api')) {
$completiontimeexpected = !empty($choicegroup->completionexpected) ? $choicegroup->completionexpected : null;
\core_completion\api::update_completion_date_event($choicegroup->coursemodule, 'choicegroup', $choicegroup->id, $completiontimeexpected);
}
-
+ if (isset($choicegroup->restrictbygrouping) && $choicegroup->restrictbygrouping) {
+ $choicegroup->restrictbygrouping = $choicegroup->restrictbygrouping;
+ } else {
+ $choicegroup->restrictbygrouping = false;
+ }
return $DB->update_record('choicegroup', $choicegroup);
@@ -316,7 +350,6 @@ function choicegroup_update_instance($choicegroup) {
* @return array
*/
function choicegroup_prepare_options($choicegroup, $user, $coursemodule, $allresponses) {
-
$cdisplay = array('options'=>array());
$cdisplay['limitanswers'] = true;
@@ -326,6 +359,11 @@ function choicegroup_prepare_options($choicegroup, $user, $coursemodule, $allres
if (!isset($choicegroup->option)) {
$choicegroup->option = [];
}
+ if (isset($choicegroup->restrictbygrouping) && $choicegroup->restrictbygrouping) {
+ $usergroupassigments = choicegroup_get_all_relevant_group_assignments($choicegroup->id);
+ $groupgrouping = choicegroup_structure_groupings_to_group($choicegroup->option);
+ }
+
foreach ($choicegroup->option as $optionid => $text) {
if (isset($text)) { //make sure there are no dud entries in the db with blank text values.
$option = new stdClass;
@@ -347,6 +385,19 @@ function choicegroup_prepare_options($choicegroup, $user, $coursemodule, $allres
}
}
}
+ // If we have grouping restrictions AND assigments to relevant groupings are not empty...
+ // - we check if group is available.
+ if ($choicegroup->restrictbygrouping) {
+ $option->groupavailable = choicegroup_check_group_available($option->groupid, $usergroupassigments,
+ $groupgrouping);
+ // If the grouping is not clickable AND we show additional information.
+ if (!($option->groupavailable) && $choicegroup->restrictchoicesbehaviour == 3) {
+ $option->conflictinggroups = choicegroup_get_conflictinggroups($option->groupid, $groupgrouping,
+ $choicegroup->id);
+ }
+ } else {
+ $option->groupavailable = CHOICEGROUP_RESTRICTGROUPINGS_AVAILABLE;
+ }
if ( $choicegroup->limitanswers && ($option->countanswers >= $option->maxanswers) && empty($option->attributes->checked)) {
$option->attributes->disabled = true;
}
@@ -363,6 +414,172 @@ function choicegroup_prepare_options($choicegroup, $user, $coursemodule, $allres
return $cdisplay;
}
+/**
+ * Collects all group memberships which are relevant for the choicegroup activity.
+ * As those query get very fast hard to understand.
+ * If a user is member in a group which belongs to a grouping which is a restriction in the choice, the user is listed.
+ * Gets all groups from the selected groupings where the current user is a member.
+ * @param int $choicegroupid
+ * @return array
+ * @throws dml_exception
+ */
+function choicegroup_get_all_relevant_group_assignments($choicegroupid) {
+ global $DB, $USER;
+ $assignments = $DB->get_records_sql('
+ SELECT gm.id as id, cg.choicegroupid as choicegroupid, cg.groupingid as groupingid, gg.groupid as groupid,
+ gm.userid as userid
+ FROM {choicegroup_groupings} as cg
+LEFT JOIN {groupings_groups} as gg on cg.groupingid = gg.groupingid
+LEFT JOIN {groups_members} as gm on gg.groupid = gm.groupid
+WHERE cg.choicegroupid=:choicegroupid AND gm.userid=:userid;',
+ array('choicegroupid' => $choicegroupid, 'userid' => $USER->id));
+ return $assignments;
+}
+
+
+/**
+ * In case the user should be informed about additional groups additional information is catched.
+ * Improvement: For sure there are more efficient DB calls.
+ * Current approach ->
+ * 1) Get all relevant groupings which is A) currentgroup is assigned to it && B) grouping is restrictioncriteria of choicegroup
+ * 2) For relevantgroupings Check for all groups if user is member - if it is not the current group it is conflicting.
+ *
+ * @param int $currentgroupid current choice group id
+ * @param array $groupgroupings only assigments from groups in the current choicegroup
+ * @param int $choicegroupid id of current mod_choicegroup
+ * @return array of conflicting group names
+ */
+function choicegroup_get_conflictinggroups($currentgroupid, $groupgroupings, $choicegroupid) {
+ global $DB, $USER;
+ // Current group does not belong to a relevant grouping.
+ if (!isset($groupgroupings[$currentgroupid])) {
+ return array();
+ }
+ // Get all groupings of group.
+ $groupings = $groupgroupings[$currentgroupid];
+ $relevantgroupings = array();
+ // Get the groupings which are mentioned in the restriction.
+ $restrictcombinations = $DB->get_records('choicegroup_groupings', array('choicegroupid' => $choicegroupid));
+ foreach ($restrictcombinations as $restrictcombination) {
+ // If the grouping is a restriction and the current group belongs to that restriction.
+ if (in_array($restrictcombination->groupingid, $groupings)) {
+ array_push($relevantgroupings, $restrictcombination->groupingid);
+ }
+ }
+ if (empty($relevantgroupings)) {
+ return array();
+ }
+ // Final Groups.
+ $conflictinggroups = array();
+
+ foreach ($relevantgroupings as $relevantgrouping) {
+ $allgroups = $DB->get_records('groupings_groups', array('groupingid' => $relevantgrouping));
+ foreach ($allgroups as $group) {
+ if (groups_is_member($group->groupid, $USER->id) && $group->groupid !== $currentgroupid) {
+ // Most likely not called very often.
+ array_push($conflictinggroups, groups_get_group_name($group->groupid));
+ }
+ }
+ }
+ return $conflictinggroups;
+}
+
+
+/**
+ * Structure Groups to Grouping to make checks easier.
+ * E.g. Group A1 with id A1, Group B with id B1
+ * Grouping A id A, Grouping B id B, Grouping 1 id 1
+ * would result in:
+ * [
+ * [A1] => [A,1]
+ * [B1] => [B,1]
+ * ]
+ * @param array $options get for each group a grouping
+ * @return array of groupid->array([groupingids])
+ * @throws coding_exception
+ * @throws dml_exception
+ */
+function choicegroup_structure_groupings_to_group($options) {
+ global $DB;
+ $relevantgroups = array();
+ // Get all groupids of choices.
+ foreach ($options as $groupid) {
+ if (isset($groupid)) {
+ array_push($relevantgroups, $groupid);
+ }
+ }
+
+ list($sqlgroup, $pgroup) = $DB->get_in_or_equal($relevantgroups);
+ $groupgroupingassigments = $DB->get_records_select('groupings_groups', 'groupid ' . $sqlgroup,
+ $pgroup, '', 'id, groupingid, groupid');
+ $structuredgroups = array();
+ // Make an index in the array for each group and add groupings.
+ foreach ($groupgroupingassigments as $groupgroupingassignment) {
+ if (!isset($structuredgroups[$groupgroupingassignment->groupid])) {
+ $structuredgroups[$groupgroupingassignment->groupid] = array();
+ }
+ array_push($structuredgroups[$groupgroupingassignment->groupid], $groupgroupingassignment->groupingid);
+ }
+ return $structuredgroups;
+}
+/**
+ * Check if a user is assigned to a group belonging to the same grouping as the option and if we have a conflict.
+ * Example : Gropu A1, A2 and B1, B2 A1 and B1 are in the same grouping
+ * Choicegroup is restricted by grouping 1 and has Group B1 B2
+ * If a student 1 is assigned to A1:
+ * Group B1 returns 1 as the group is not available
+ * Group B2 return 0 as the group is available to choose
+ * If student 2 is assigned to A1 and B1:
+ * The function returns 2 for group B1 as we have a real conflict - more assigment than allowed in one grouping.
+ *
+ * @param int $optiongroupid id of group
+ * @param array $assignments groups a user is assigned to
+ * @param array $structuredgroups array groupid => array([groupingsids])
+ * @return int 1 -> No Problem 0 -> group not available 2 -> CONFLICT
+ * @throws dml_exception
+ */
+function choicegroup_check_group_available($optiongroupid, $assignments, $structuredgroups) {
+ $owngroup = false;
+ $uniquegroups = [];
+ foreach ($assignments as $assigment) {
+ // Checks: 1) Do we have groupings to the current group
+ // 2) Gehört die Zuweisung zu einer Gruppierung die für die option relevant ist?
+ // 3) ist die zurzeitige
+ if (array_key_exists($optiongroupid, $structuredgroups) &&
+ in_array($assigment->groupingid, $structuredgroups[$optiongroupid]) && $assigment->groupid !== $optiongroupid) {
+ $uniquegroups[$assigment->groupid] = $assigment->groupid;
+ }
+ if ($assigment->groupid == $optiongroupid) {
+ $owngroup = true;
+ $uniquegroups[$assigment->groupid] = $assigment->groupid;
+ }
+ }
+
+ $counter = count($uniquegroups);
+
+ if (!($owngroup)) {
+ if ($counter == 0) {
+ return CHOICEGROUP_RESTRICTGROUPINGS_AVAILABLE;
+ }
+ if ($counter == 1) {
+ return CHOICEGROUP_RESTRICTGROUPINGS_NOTAVAILABLE;
+ } else if ($counter > 1) {
+ return CHOICEGROUP_RESTRICTGROUPINGS_CONFLICT;
+ }
+ } else {
+ if ($counter == 0) {
+ return CHOICEGROUP_RESTRICTGROUPINGS_AVAILABLE;
+ }
+ if ($counter == 1) {
+ return CHOICEGROUP_RESTRICTGROUPINGS_AVAILABLE;
+ }
+ if ($counter > 1) {
+ return CHOICEGROUP_RESTRICTGROUPINGS_CONFLICT;
+ }
+ }
+
+ return CHOICEGROUP_RESTRICTGROUPINGS_AVAILABLE;
+}
/**
* @throws \moodle_exception
*/
@@ -755,6 +972,9 @@ function choicegroup_delete_instance($id) {
if (! $DB->delete_records("choicegroup_options", array("choicegroupid"=>"$choicegroup->id"))) {
$result = false;
}
+ if (! $DB->delete_records("choicegroup_groupings", array("choicegroupid" => "$choicegroup->id"))) {
+ $result = false;
+ }
if (! $DB->delete_records("choicegroup", array("id"=>"$choicegroup->id"))) {
$result = false;
diff --git a/mod_form.php b/mod_form.php
index 811295f..5f809c5 100644
--- a/mod_form.php
+++ b/mod_form.php
@@ -254,6 +254,38 @@ function definition()
$mform->addElement('date_time_selector', 'timeclose', get_string("choicegroupclose", "choicegroup"));
$mform->disabledIf('timeclose', 'timerestrict');
+ // -------------------------
+ // Enable restrictions by grouping over multiple mod_choicegroup instances.
+ // -------------------------
+
+ $mform->addElement('header', 'restrictbygroupinghdr', get_string('groupingrestrict', 'choicegroup'));
+ $mform->addElement('checkbox', 'restrictbygrouping', get_string('groupingrestrict', 'choicegroup'));
+
+ $arrayofgroupings = array();
+ foreach ($db_groupings as $dbgrouping) {
+ $arrayofgroupings[$dbgrouping->id] = $dbgrouping->name;
+ }
+ $groupingdefault = array();
+ if (isset($this->current->id) && $this->current->id != '') {
+ $selectedgropings = $DB->get_records('choicegroup_groupings', array('choicegroupid' => $this->current->id));
+ foreach ($selectedgropings as $selectedgrouping) {
+ array_push($groupingdefault, $selectedgrouping->groupingid);
+ }
+ }
+ $select = $mform->addElement('select', 'selectedgroupings', get_string('grouping', 'group'),
+ $arrayofgroupings);
+ $mform->setDefault('selectedgroupings', $groupingdefault);
+ $mform->disabledIf('selectedgroupings', 'restrictbygrouping');
+ $mform->addHelpButton('selectedgroupings', 'selectedgroupings', 'mod_choicegroup');
+ $select->setMultiple(true);
+
+ $behaviouroptions = array(get_string('hidegroup', 'mod_choicegroup'),
+ get_string('dimmgroup', 'mod_choicegroup'),
+ get_string('informlimit', 'mod_choicegroup'),
+ get_string('informlimitgroup', 'mod_choicegroup'));
+ $mform->addElement('select', 'restrictchoicesbehaviour', get_string('limitationbehaviour', 'mod_choicegroup'), $behaviouroptions);
+ $mform->disabledIf('restrictchoicesbehaviour', 'restrictbygrouping');
+ $mform->addHelpButton('restrictchoicesbehaviour', 'restrictchoicesbehaviour', 'mod_choicegroup');
//-------------------------------------------------------------------------------
$this->standard_coursemodule_elements();
//-------------------------------------------------------------------------------
diff --git a/renderer.php b/renderer.php
index 02c6ee1..6d283bb 100644
--- a/renderer.php
+++ b/renderer.php
@@ -46,8 +46,8 @@ class mod_choicegroup_renderer extends plugin_renderer_base {
*
* @return string
*/
- public function display_options($options, $coursemoduleid, $vertical = true, $publish = false, $limitanswers = false, $showresults = false, $current = false, $choicegroupopen = false, $disabled = false, $multipleenrollmentspossible = false, $onlyactive = false) {
- global $DB, $PAGE, $choicegroup_groups;
+ public function display_options($options, $coursemoduleid, $vertical = true, $publish = false, $limitanswers = false, $showresults = false, $current = false, $choicegroupopen = false, $disabled = false, $multipleenrollmentspossible = false, $onlyactive = false, $restrictchoicesbehaviour = 0) {
+ global $DB, $PAGE, $OUTPUT, $choicegroup_groups;
$target = new moodle_url('/mod/choicegroup/view.php');
$attributes = array('method'=>'POST', 'action'=>$target, 'class'=> 'tableform');
@@ -85,6 +85,13 @@ public function display_options($options, $coursemoduleid, $vertical = true, $pu
$answer_to_groupid_mappings = '';
}
$initiallyHideSubmitButton = false;
+ // If we have a conflict do not show the form.
+ foreach ($options['options'] as $option) {
+ if (isset($option->groupavailable) && $option->groupavailable == 2) {
+ return html_writer::div($OUTPUT->notification(get_string('conflictinggroupassignment', 'choicegroup'),
+ 'notifyproblem', false), 'choicegrouprestrictionerror');
+ }
+ }
foreach ($options['options'] as $option) {
$group = (isset($choicegroup_groups[$option->groupid])) ? ($choicegroup_groups[$option->groupid]) : (false);
if (!$group) {
@@ -99,6 +106,13 @@ public function display_options($options, $coursemoduleid, $vertical = true, $pu
$html .= html_writer::tag('tr', $cell);
break;
}
+ $context = \context_course::instance($group->courseid);
+
+ // Shows users with readcapability also "hidden" choices.
+ if (isset($option->groupavailable) && $option->groupavailable == 0 && $restrictchoicesbehaviour == 0 &&
+ !(has_capability('mod/choicegroup:readresponses', $context))) {
+ continue;
+ }
$html .= html_writer::start_tag('tr', array('class'=>'option'));
$html .= html_writer::start_tag('td', array('class'=>'center'));
@@ -115,7 +129,6 @@ public function display_options($options, $coursemoduleid, $vertical = true, $pu
}
}
- $context = \context_course::instance($group->courseid);
$labeltext = html_writer::tag('label', format_string($group->name), array('for' => 'choiceid_' . $option->attributes->value));
$group_members = get_enrolled_users($context, '', $group->id, 'u.*', 'u.lastname, u.firstname', 0, 0, $onlyactive);
$group_members_names = array();
@@ -127,6 +140,33 @@ public function display_options($options, $coursemoduleid, $vertical = true, $pu
$option->attributes->disabled=true;
$availableoption--;
}
+ // In case the group is not available due to grouping restrictions,
+ // we need to change the depiction of the choice.
+ if (isset($option->groupavailable) && $option->groupavailable == 0) {
+ // Show general info.
+ if ($restrictchoicesbehaviour == 2) {
+ $labeltext .= '
' . html_writer::tag('em', get_string('assignedtogrouping', 'choicegroup'));
+ } else if ($restrictchoicesbehaviour == 3) {
+ // Show which groups conflict.
+ if (count($option->conflictinggroups) > 1) {
+ $labeltext .= '
' . html_writer::tag('em', get_string('reasongrouplimitationplural',
+ 'choicegroup', implode(', ', $option->conflictinggroups)));
+ } else if (count($option->conflictinggroups) == 1){
+ $labeltext .= '
' . html_writer::tag('em', get_string('reasongrouplimitationsingular',
+ 'choicegroup', implode('', $option->conflictinggroups)));
+ } else {
+ // Should not happen no conflicting group found.
+ $labeltext .= '
' . html_writer::tag('em', get_string('reasongrouplimitationempty',
+ 'choicegroup', implode('', $option->conflictinggroups)));
+ }
+ } else if (has_capability('mod/choicegroup:readresponses', $context) && $restrictchoicesbehaviour == 0) {
+ // In case we have a "managing" person, he/she still gets to see the choices...
+ // but with additional information.
+ $labeltext .= '
' . html_writer::tag('em', get_string('hidechoicetostudents', 'choicegroup'));
+ }
+ $option->attributes->disabled = true;
+ $availableoption--;
+ }
$labeltext .= html_writer::tag('div', format_text(file_rewrite_pluginfile_urls($group->description,
'pluginfile.php',
$context->id,
diff --git a/tests/behat/choicegroup_groupingsrestriction.feature b/tests/behat/choicegroup_groupingsrestriction.feature
new file mode 100644
index 0000000..656b3f7
--- /dev/null
+++ b/tests/behat/choicegroup_groupingsrestriction.feature
@@ -0,0 +1,159 @@
+@mod @mod_choicegroup @mod_choicegroup_groupingrestriction
+Feature: In case we have a grouping restriction conflicting choices are not shown in the choicegroup activity.
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | student1 | Vinnie | Student1 | student1@example.com |
+ | student2 | Ann | Student2 | student2@example.com |
+ | teacher1 | Darrell | Teacher1 | teacher1@example.com |
+ And the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ | teacher1 | C1 | editingteacher |
+ And the following "groups" exist:
+ | name | course | idnumber |
+ | Group A1 | C1 | GGA1 |
+ | Group A2 | C1 | GGA2 |
+ | Group B1 | C1 | GGB1 |
+ | Group B2 | C1 | GGB2 |
+ And the following "groupings" exist:
+ | name | course | idnumber |
+ | Grouping A | C1 | GA |
+ | Grouping B | C1 | GB |
+ | Grouping 1 | C1 | G1 |
+ | Grouping 2 | C1 | G2 |
+ And the following "grouping groups" exist:
+ | grouping | group |
+ | GA | GGA1 |
+ | GA | GGA2 |
+ | GB | GGB1 |
+ | GB | GGB2 |
+ | G1 | GGA1 |
+ | G1 | GGB1 |
+ | G2 | GGB2 |
+ Given I log in as "teacher1"
+ And I am on "Course 1" course homepage with editing mode on
+ # Create a new Group Choice with Group A1 available.
+ And I press "Add an activity or resource"
+ And I click on "Add a new Group choice" "link" in the "Add an activity or resource" "dialogue"
+ And I set the following fields to these values:
+ | Group choice name | Choose your group |
+ And I set the field "availablegroups" to "Grouping A"
+ And I set the field "availablegroups" to "Group A1"
+ And I press "Add Group"
+ And I press "Save and return to course"
+ And I am on "Course 1" course homepage with editing mode on
+ And I press "Add an activity or resource"
+ # Create a new Group Choice with Group B1,2 available and restricted.
+ And I click on "Add a new Group choice" "link" in the "Add an activity or resource" "dialogue"
+ And I click on "collapseElement-4" "link"
+ And I set the following fields to these values:
+ | Group choice name | Choose second group |
+ | restrictbygrouping| 1 |
+ And I set the field "availablegroups" to "Grouping B"
+ And I press "Add Grouping"
+ And I set the field "id_selectedgroupings" to "Grouping 1"
+ And I set the field "id_restrictchoicesbehaviour" to "Hide group from group list"
+ And I press "Save and return to course"
+ And I log out
+
+ @javascript
+ Scenario: Basic Test - I can still select a choice (normally use the choicegroup activity)
+ without the groupingrestriction feature.
+ Given I log in as "student1"
+ And I am on "Course 1" course homepage
+ And I am on the "Choose your group" "choicegroup activity" page logged in as student1
+ And I should see "Group A1" in the ".choicegroups" "css_element"
+ And I click on "//table[@class='choicegroups']/tbody/tr[2]/td[1]/input" "xpath_element"
+ And I click on "Save my choice" "button"
+ And I should see "Your selection: Group A1" in the "#yourselection" "css_element"
+
+ @javascript
+ Scenario: Basic Test - I can still select a choice (normally use the choicegroup activity) without the groupingrestriction feature.
+ Given I log in as "student1"
+ And I am on "Course 1" course homepage
+ And I am on the "Choose your group" "choicegroup activity" page logged in as student1
+ And I should see "Group A1" in the ".choicegroups" "css_element"
+ And I click on "//table[@class='choicegroups']/tbody/tr[2]/td[1]/input" "xpath_element"
+ And I click on "Save my choice" "button"
+ And I am on "Course 1" course homepage
+ And I am on the "Choose second group" "choicegroup activity" page logged in as student1
+ Then I should not see "Group B1" in the ".choicegroups" "css_element"
+ And I should see "Group B2" in the ".choicegroups" "css_element"
+
+ @javascript
+ Scenario Outline: With two choicegroup activities (one restricted to Groping1 and with hide group limitationbehaviour)
+ student1 assigned to A1 can not see B1, student2 who is assigned to GGB1 can see B1 and A1 (as not restircted)
+ Given the following "group members" exist:
+ | user | group |
+ | | |
+ # As a student assigned to A1 I do see A1, B2 and NOT B1. As a student assigned to B1 I can see all since
+ # 1. mod_choicegroup is not restricted!
+ And I log in as ""
+ And I am on "Course 1" course homepage
+ And I am on the "Choose your group" "choicegroup activity" page logged in as
+ And I should "Group A1" in the ".choicegroups" "css_element"
+ And I am on the "Choose second group" "choicegroup activity" page logged in as
+ And I should "Group B1" in the ".choicegroups" "css_element"
+ And I should see "Group B2" in the ".choicegroups" "css_element"
+
+ Examples:
+ | user | groupassign | B1visible | A1visible |
+ | student1 | GGA1 | not see | see |
+ | student2 | GGB1 | see | see |
+
+ @javascript
+ Scenario Outline: Depending on the visibility of the activity different layouts are shown
+ Given the following "group members" exist:
+ | user | group |
+ | student1 | GGA1 |
+ And I log in as "teacher1"
+ And I am on "Course 1" course homepage with editing mode on
+ And I open "Choose second group" actions menu
+ And I click on "Edit settings" "link" in the "Choose second group" activity
+ And I click on "collapseElement-4" "link"
+ And I set the field "id_restrictchoicesbehaviour" to ""
+ And I press "Save and return to course"
+ And I log out
+ And I log in as "student1"
+ And I am on "Course 1" course homepage
+ And I am on the "Choose your group" "choicegroup activity" page logged in as student1
+ And I should see "Group A1" in the ".choicegroups" "css_element"
+ And I am on "Course 1" course homepage
+ And I am on the "Choose second group" "choicegroup activity" page logged in as student1
+ And I should see "Group B1" in the ".choicegroups" "css_element"
+ And I should see "Group B2" in the ".choicegroups" "css_element"
+ And I should "(You are already assigned to a group which contradicts this choice)" in the ".choicegroups" "css_element"
+
+ Examples:
+ | limitationbehaviour | seenotice |
+ | Show group as dimmed | not see |
+ | Show group with limitation notice | see |
+
+ @javascript
+ Scenario: With a choicegroup activities restricted to a grouping the teacher sees all choices.
+ Given I log in as "teacher1"
+ And I am on "Course 1" course homepage with editing mode on
+ # As a teacher without group assignment I see all choices.
+ And I am on the "Choose your group" "choicegroup activity" page logged in as teacher1
+ Then I should see "Group A1" in the ".choicegroups" "css_element"
+ And I am on the "Choose second group" "choicegroup activity" page logged in as teacher1
+ And I should see "Group B1" in the ".choicegroups" "css_element"
+ And I should see "Group B2" in the ".choicegroups" "css_element"
+
+ @javascript
+ Scenario: Corner Case illegal assignment
+ Given the following "group members" exist:
+ | user | group |
+ | student1 | GGA1 |
+ | student1 | GGB1 |
+ And I log in as "student1"
+ And I am on the "Choose your group" "choicegroup activity" page logged in as student1
+ Then I should see "Group A1" in the ".choicegroups" "css_element"
+ And I am on the "Choose second group" "choicegroup activity" page logged in as student1
+ And I should see "Your Group assigment are conflicting. Therefore you can not participate in this groupchoice activity." in the ".choicegrouprestrictionerror" "css_element"
diff --git a/version.php b/version.php
index 9dd7e4d..5fdd70d 100644
--- a/version.php
+++ b/version.php
@@ -26,7 +26,7 @@
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2023010300;
+$plugin->version = 2023011600.03;
$plugin->requires = 2020061500; // Moodle 3.9
$plugin->maturity = MATURITY_STABLE;
$plugin->release = '1.39 for Moodle 3.9-4.1 (Build: 2023010300)';
diff --git a/view.php b/view.php
index 30a269c..a4ff437 100644
--- a/view.php
+++ b/view.php
@@ -99,7 +99,11 @@
/// Submit any new data if there is any
if (data_submitted() && is_enrolled($context, null, 'mod/choicegroup:choose') && confirm_sesskey()) {
-
+ // Get data once.
+ if ($choicegroup->restrictbygrouping) {
+ $usergroupassigments = choicegroup_get_all_relevant_group_assignments($choicegroup->id);
+ $groupgrouping = choicegroup_structure_groupings_to_group($choicegroup->option);
+ }
if ($choicegroup->multipleenrollmentspossible == 1) {
$number_of_groups = optional_param('number_of_groups', '', PARAM_INT);
$enrollmentscount = 0;
@@ -120,6 +124,15 @@
for ($i = 0; $i < $number_of_groups; $i++) {
$answer_value = optional_param('answer_' . $i, '', PARAM_INT);
if ($answer_value != '') {
+ // Check finally if the group is still available.
+ if ($choicegroup->restrictbygrouping && !(empty($usergroupassigments))) {
+ $choicerecord = $DB->get_record('choicegroup_options', array('id' => $answer_value), '*', MUST_EXIST);
+ $groupavailable = choicegroup_check_group_available($choicerecord->groupid, $usergroupassigments, $groupgrouping);
+ if (!$groupavailable) {
+ redirect(new moodle_url('/mod/choicegroup/view.php',
+ array('id' => $cm->id, 'notify' => 'submitfailedgroupingrestriction', 'sesskey' => sesskey())));
+ }
+ }
choicegroup_user_submit_response($answer_value, $choicegroup, $USER->id, $course, $cm);
} else {
$answer_value_group_id = optional_param('answer_'.$i.'_groupid', '', PARAM_INT);
@@ -152,6 +165,15 @@
redirect(new moodle_url('/mod/choicegroup/view.php',
array('id' => $cm->id, 'notify' => 'mustchooseone', 'sesskey' => sesskey())));
} else {
+ // Check finally if the group is still available.
+ if ($choicegroup->restrictbygrouping && !(empty($usergroupassigments))) {
+ $choicerecord = $DB->get_record('choicegroup_options', array('id' => $answer), '*', MUST_EXIST);
+ $groupavailable = choicegroup_check_group_available($choicerecord->groupid, $usergroupassigments, $groupgrouping);
+ if (!$groupavailable) {
+ redirect(new moodle_url('/mod/choicegroup/view.php',
+ array('id' => $cm->id, 'notify' => 'submitfailedgroupingrestriction', 'sesskey' => sesskey())));
+ }
+ }
choicegroup_user_submit_response($answer, $choicegroup, $USER->id, $course, $cm);
redirect(new moodle_url('/mod/choicegroup/view.php',
array('id' => $cm->id, 'notify' => 'choicegroupsaved', 'sesskey' => sesskey())));
@@ -180,6 +202,8 @@
echo $OUTPUT->notification(get_string('mustchooseone', 'choicegroup'), 'notifyproblem');
} else if ($notify === 'mustchoosemax') {
echo $OUTPUT->notification(get_string('mustchoosemax', 'choicegroup', $choicegroup->maxenrollments), 'notifyproblem');
+ } else if ($notify === 'submitfailedgroupingrestriction') {
+ echo $OUTPUT->notification(get_string('submitfailedgroupingrestriction', 'choicegroup'), 'notifyproblem');
}
}
@@ -256,10 +280,10 @@
if ( (!$current or $choicegroup->allowupdate) and $choicegroupopen and is_enrolled($context, null, 'mod/choicegroup:choose')) {
// They haven't made their choicegroup yet or updates allowed and choicegroup is open
- echo $renderer->display_options($options, $cm->id, $choicegroup->display, $choicegroup->publish, $choicegroup->limitanswers, $choicegroup->showresults, $current, $choicegroupopen, false, $choicegroup->multipleenrollmentspossible, $choicegroup->onlyactive);
+ echo $renderer->display_options($options, $cm->id, $choicegroup->display, $choicegroup->publish, $choicegroup->limitanswers, $choicegroup->showresults, $current, $choicegroupopen, false, $choicegroup->multipleenrollmentspossible, $choicegroup->onlyactive, $choicegroup->restrictchoicesbehaviour);
} else {
// form can not be updated
- echo $renderer->display_options($options, $cm->id, $choicegroup->display, $choicegroup->publish, $choicegroup->limitanswers, $choicegroup->showresults, $current, $choicegroupopen, true, $choicegroup->multipleenrollmentspossible, $choicegroup->onlyactive);
+ echo $renderer->display_options($options, $cm->id, $choicegroup->display, $choicegroup->publish, $choicegroup->limitanswers, $choicegroup->showresults, $current, $choicegroupopen, true, $choicegroup->multipleenrollmentspossible, $choicegroup->onlyactive, $choicegroup->restrictchoicesbehaviour);
}
$choicegroupformshown = true;