Skip to content

Commit af644c7

Browse files
authored
Merge pull request #230 from catalyst/issue199-MOODLE_403_STABLE-custom-field-visibility
Issue #199: Custom field visibility (#213)
2 parents f0775d8 + 9a3e883 commit af644c7

File tree

9 files changed

+238
-5
lines changed

9 files changed

+238
-5
lines changed

classes/customfield_form.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ public function definition() {
7474
$mform->addElement('checkbox', 'showinsummary', get_string('setting:showinsummary', 'facetoface'));
7575
$mform->setDefault('showinsummary', true);
7676

77+
// Field data visibility.
78+
$visibilityoptions = [
79+
MDL_F2F_FIELD_VISIBLETOALL => get_string('customfield_visibletoall', 'facetoface'),
80+
MDL_F2F_FIELD_VISIBLETOTEACHERS => get_string('customfield_visibletoteachers', 'facetoface'),
81+
MDL_F2F_FIELD_NOTVISIBLE => get_string('customfield_notvisible', 'facetoface')
82+
];
83+
$mform->addElement('select', 'visibleto',
84+
get_string('customfield_visibility', 'facetoface'),
85+
$visibilityoptions);
86+
$mform->addHelpButton(
87+
'visibleto', 'customfield_visibility', 'facetoface');
88+
7789
$this->add_action_buttons();
7890
}
7991

customfield.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
$todb->required = $fromform->required;
140140
$todb->isfilter = $fromform->isfilter;
141141
$todb->showinsummary = $fromform->showinsummary;
142+
$todb->visibleto = $fromform->visibleto;
142143

143144
if ($field != null) {
144145
$todb->id = $field->id;
@@ -165,6 +166,7 @@
165166
$toform->required = ($field->required == 1);
166167
$toform->isfilter = ($field->isfilter == 1);
167168
$toform->showinsummary = ($field->showinsummary == 1);
169+
$toform->visibleto = $field->visibleto;
168170

169171
$mform->set_data($toform);
170172
}

db/install.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
<FIELD NAME="defaultvalue" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
135135
<FIELD NAME="isfilter" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Whether or not this field is a filter on the Training Calendar"/>
136136
<FIELD NAME="showinsummary" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Whether or not to show this field in attendance exports and lists of sessions"/>
137+
<FIELD NAME="visibleto" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Whether or not to show this field in lists of sessions"/>
137138
</FIELDS>
138139
<KEYS>
139140
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>

db/upgrade.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,5 +859,37 @@ function xmldb_facetoface_upgrade($oldversion=0) {
859859
upgrade_mod_savepoint(true, 2023100200, 'facetoface');
860860
}
861861

862+
if ($oldversion < 2025072400) {
863+
864+
// Define field visibleto to be added to facetoface_session_field
865+
$table = new xmldb_table('facetoface_session_field');
866+
$field = new xmldb_field(
867+
'visibleto',
868+
XMLDB_TYPE_INTEGER,
869+
'1',
870+
null,
871+
XMLDB_NOTNULL,
872+
null,
873+
MDL_F2F_FIELD_VISIBLETOALL,
874+
'showinsummary'
875+
);
876+
877+
// Conditionally launch add field visibleto.
878+
if (!$dbman->field_exists($table, $field)) {
879+
$dbman->add_field($table, $field);
880+
881+
// Get all existing records and set visibleto based on showinsummary value.
882+
$recordset = $DB->get_recordset('facetoface_session_field');
883+
foreach ($recordset as $record) {
884+
$record->visibleto = empty($record->showinsummary) ? MDL_F2F_FIELD_NOTVISIBLE : MDL_F2F_FIELD_VISIBLETOALL;
885+
$DB->update_record('facetoface_session_field', $record);
886+
}
887+
$recordset->close;
888+
}
889+
890+
// Face-to-face savepoint reached
891+
upgrade_mod_savepoint(true, 2025072400, 'facetoface');
892+
}
893+
862894
return $result;
863895
}

lang/en/facetoface.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@
575575
$string['setting:hidediscount_caption'] = 'Hide discount:';
576576
$string['setting:isfilter'] = 'Display as a filter';
577577
$string['setting:possiblevalues'] = 'List of possible values';
578-
$string['setting:showinsummary'] = 'Show in exports and lists';
578+
$string['setting:showinsummary'] = 'Show in exports';
579579
$string['setting:sessionroles'] = 'Users assigned to the selected roles in a course can be tracked with each Face-to-Face session';
580580
$string['setting:sessionroles_caption'] = 'Session roles:';
581581
$string['setting:type'] = 'Field type';
@@ -838,3 +838,8 @@
838838
$string['addtoallsessions'] = 'Add users to all (upcoming) sessions';
839839
$string['addtoallsessions_help'] = 'Use this option if you want to add users to all upcoming Face-to-Face sessions. When this uption is toggled, the selected users will be added to this session and all other future sessions in the activity.';
840840
$string['caseinsensitive'] = 'Case insensitive';
841+
$string['customfield_notvisible'] = 'Nobody';
842+
$string['customfield_visibility'] = 'Visible to';
843+
$string['customfield_visibility_help'] = 'This setting determines who can view the custom field name and value in the list sessions in the module page or when signing up for a session.';
844+
$string['customfield_visibletoall'] = 'Everyone';
845+
$string['customfield_visibletoteachers'] = 'Teachers';

lib.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@
8989
define('MDL_F2F_STATUS_PARTIALLY_ATTENDED', 90);
9090
define('MDL_F2F_STATUS_FULLY_ATTENDED', 100);
9191

92+
define('MDL_F2F_FIELD_VISIBLETOALL', 2);
93+
define('MDL_F2F_FIELD_VISIBLETOTEACHERS', 1);
94+
define('MDL_F2F_FIELD_NOTVISIBLE', 0);
95+
9296
/**
9397
* Returns the list of possible facetoface status.
9498
* @return array $string Human readable code
@@ -3640,7 +3644,11 @@ function facetoface_print_session($session, $showcapacity, $calendaroutput=false
36403644

36413645
$customfields = facetoface_get_session_customfields();
36423646
$customdata = $DB->get_records('facetoface_session_data', ['sessionid' => $session->id], '', 'fieldid, data');
3647+
$editsessions = has_capability('mod/facetoface:editsessions', context_system::instance());
36433648
foreach ($customfields as $field) {
3649+
if (!facetoface_can_view_field($field->visibleto, $editsessions)) {
3650+
continue;
3651+
}
36443652
$data = '';
36453653
if (!empty($customdata[$field->id])) {
36463654
if (CUSTOMFIELD_TYPE_MULTISELECT == $field->type) {
@@ -4324,6 +4332,23 @@ function facetoface_cancellation_allowed(stdClass $session): bool {
43244332
return $timenow <= ($sessionstart - $cancelrestriction);
43254333
}
43264334

4335+
/**
4336+
* The current user can view custom fields.
4337+
*
4338+
* @param int $visibility whether field can be seen by all, teachers, or none
4339+
* @param bool $editsessions id of the question to test edit permission
4340+
* @return bool true if the current user can view custom fields, false otherwise
4341+
*/
4342+
function facetoface_can_view_field(int $visibility, bool $editsessions): bool {
4343+
if ($visibility == MDL_F2F_FIELD_VISIBLETOALL) {
4344+
return true;
4345+
} else if ($visibility == MDL_F2F_FIELD_VISIBLETOTEACHERS) {
4346+
return $editsessions;
4347+
} else {
4348+
return false;
4349+
}
4350+
}
4351+
43274352
/*
43284353
* facetoface assignment candidates
43294354
*/

renderer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function print_session_list_table(
4545

4646
$tableheader = [];
4747
foreach ($customfields as $field) {
48-
if (!empty($field->showinsummary)) {
48+
if (facetoface_can_view_field($field->visibleto, $editsessions)) {
4949
$tableheader[] = format_string($field->name);
5050
}
5151
}
@@ -82,7 +82,7 @@ public function print_session_list_table(
8282
// Custom fields.
8383
$customdata = $session->customfielddata;
8484
foreach ($customfields as $field) {
85-
if (empty($field->showinsummary)) {
85+
if (!facetoface_can_view_field($field->visibleto, $editsessions)) {
8686
continue;
8787
}
8888

tests/session_test.php

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,160 @@ public function test_cancellation_allowed(
418418
$result = facetoface_cancellation_allowed($session);
419419
$this->assertEquals($expected, $result);
420420
}
421+
422+
/**
423+
* Data provider for test_field_visibility
424+
*
425+
* @return array
426+
*/
427+
public static function field_visibility_provider(): array {
428+
return [
429+
'visible to all, not teacher' => [
430+
'visibility' => MDL_F2F_FIELD_VISIBLETOALL,
431+
'isteacher' => false,
432+
'expected' => true,
433+
],
434+
'visible to all, is teacher' => [
435+
'visibility' => MDL_F2F_FIELD_VISIBLETOALL,
436+
'isteacher' => true,
437+
'expected' => true,
438+
],
439+
'visible to teachers only, not teacher' => [
440+
'visibility' => MDL_F2F_FIELD_VISIBLETOTEACHERS,
441+
'isteacher' => false,
442+
'expected' => false,
443+
],
444+
'visible to teachers only, is teacher' => [
445+
'visibility' => MDL_F2F_FIELD_VISIBLETOTEACHERS,
446+
'isteacher' => true,
447+
'expected' => true,
448+
],
449+
'not visible, not teacher' => [
450+
'visibility' => MDL_F2F_FIELD_NOTVISIBLE,
451+
'isteacher' => false,
452+
'expected' => false,
453+
],
454+
'not visible, is teacher' => [
455+
'visibility' => MDL_F2F_FIELD_NOTVISIBLE,
456+
'isteacher' => true,
457+
'expected' => false,
458+
],
459+
];
460+
}
461+
462+
/**
463+
* Test field visibility for different user roles and visibility settings.
464+
*
465+
* @dataProvider field_visibility_provider
466+
*/
467+
public function test_field_visibility_helper(
468+
int $visibility,
469+
bool $isteacher,
470+
bool $expected
471+
): void {
472+
global $DB, $CFG;
473+
require_once($CFG->dirroot . '/mod/facetoface/lib.php');
474+
475+
// Setup course and participants.
476+
$generator = $this->getDataGenerator()->get_plugin_generator('mod_facetoface');
477+
$course = $this->getDataGenerator()->create_course();
478+
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
479+
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
480+
$facetoface = $generator->create_instance(['course' => $course->id]);
481+
482+
// Create custom field.
483+
$customfield = (object)[
484+
'name' => 'Test Field',
485+
'shortname' => 'testfield',
486+
'type' => 0,
487+
'possiblevalues' => '',
488+
'required' => 0,
489+
'defaultvalue' => '',
490+
'isfilter' => 0,
491+
'showinsummary' => 1,
492+
'visibleto' => $visibility,
493+
];
494+
$DB->insert_record('facetoface_session_field', $customfield);
495+
496+
// Test visibility.
497+
$this->setUser($isteacher ? $teacher : $student);
498+
$canedit = has_capability('mod/facetoface:editsessions', \context_course::instance($course->id));
499+
$result = facetoface_can_view_field($visibility, $canedit);
500+
$this->assertEquals($expected, $result);
501+
}
502+
503+
/**
504+
* Test field visibility for different user roles and visibility settings.
505+
*
506+
* @dataProvider field_visibility_provider
507+
*/
508+
public function test_field_visibility(int $visibility, bool $isteacher, bool $expected): void {
509+
global $DB, $PAGE;
510+
$this->resetAfterTest();
511+
512+
// Setup course and participants.
513+
$course = $this->getDataGenerator()->create_course();
514+
if ($isteacher) {
515+
$user = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
516+
} else {
517+
$user = $this->getDataGenerator()->create_and_enrol($course, 'student');
518+
}
519+
$generator = $this->getDataGenerator()->get_plugin_generator('mod_facetoface');
520+
$facetoface = $generator->create_instance(['course' => $course->id]);
521+
522+
// Create custom field.
523+
$field = new \stdClass();
524+
$field->name = 'Test Field';
525+
$field->shortname = 'testfield';
526+
$field->type = 0;
527+
$field->required = 0;
528+
$field->isfilter = 1;
529+
$field->showinsummary = 1;
530+
$field->visibleto = $visibility;
531+
$field->id = $DB->insert_record('facetoface_session_field', $field);
532+
533+
// Create a session.
534+
$session = $generator->create_session([
535+
'facetoface' => $facetoface->id,
536+
'capacity' => 10,
537+
'sessiondates' => [
538+
[
539+
'timestart' => time() + DAYSECS,
540+
'timefinish' => time() + DAYSECS + HOURSECS,
541+
],
542+
],
543+
]);
544+
545+
// Add data for the custom field.
546+
$data = new \stdClass();
547+
$data->sessionid = $session->id;
548+
$data->fieldid = $field->id;
549+
$data->data = "Test value";
550+
$DB->insert_record('facetoface_session_data', $data);
551+
552+
// Set up session data as expected by renderer.
553+
$customfields = $DB->get_records('facetoface_session_field');
554+
$session->customfielddata = $DB->get_records(
555+
'facetoface_session_data',
556+
['sessionid' => $session->id],
557+
'',
558+
'fieldid,data'
559+
);
560+
// Add bookedsession property required by renderer but not added by generator.
561+
$session->bookedsession = null;
562+
$sessions = [$session];
563+
$renderer = $PAGE->get_renderer('mod_facetoface');
564+
565+
// Test visibility.
566+
$this->setUser($user);
567+
$canedit = has_capability('mod/facetoface:editsessions', \context_course::instance($course->id));
568+
$output = $renderer->print_session_list_table($customfields, $sessions, false, $canedit);
569+
if ($expected) {
570+
$this->assertStringContainsString('Test Field', $output);
571+
$this->assertStringContainsString('Test value', $output);
572+
} else {
573+
$this->assertStringNotContainsString('Test Field', $output);
574+
$this->assertStringNotContainsString('Test value', $output);
575+
}
576+
}
421577
}

version.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030

3131
defined('MOODLE_INTERNAL') || die();
3232

33-
$plugin->version = 2025071800;
34-
$plugin->release = 2025071800;
33+
$plugin->version = 2025072400;
34+
$plugin->release = 2025072400;
3535
$plugin->requires = 2023100900; // Requires 4.3.
3636
$plugin->component = 'mod_facetoface';
3737
$plugin->maturity = MATURITY_STABLE;

0 commit comments

Comments
 (0)