################################################################################
# WeBWorK Online Homework Delivery System
# Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/
# $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm,v 1.77 2006/01/10 00:33:19 sh002i Exp $
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
# Artistic License for more details.
################################################################################
package WeBWorK::ContentGenerator::Instructor::UserList;
use base qw(WeBWorK::ContentGenerator::Instructor);
=head1 NAME
WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific
data editing
=cut
=for comment
What do we want to be able to do here?
Filter what users are shown:
- none, all, selected
- matching user_id, matching section, matching recitation
Switch from view mode to edit mode:
- showing visible users
- showing selected users
Switch from edit mode to view and save changes
Switch from edit mode to view and abandon changes
Switch from view mode to password mode:
- showing visible users
- showing selected users
Switch from password mode to view and save changes
Switch from password mode to view and abandon changes
Delete users:
- visible
- selected
Import users:
- replace:
- any users
- visible users
- selected users
- no users
- add:
- any users
- no users
Export users:
- export:
- all
- visible
- selected
- to:
- existing file on server (overwrite): [ list of files ]
- new file on server (create): [ filename ]
=cut
use strict;
use warnings;
use CGI qw();
use WeBWorK::File::Classlist;
use WeBWorK::Utils qw(readFile readDirectory cryptPassword);
use WeBWorK::Authen qw(checkKey);
use Apache::Constants qw(:common REDIRECT DONE); #FIXME -- this should be called higher up in the object tree.
use constant HIDE_USERS_THRESHHOLD => 200;
use constant EDIT_FORMS => [qw(cancelEdit saveEdit)];
use constant PASSWORD_FORMS => [qw(cancelPassword savePassword)];
use constant VIEW_FORMS => [qw(filter sort edit password import export add delete)];
# permissions needed to perform a given action
use constant FORM_PERMS => {
saveEdit => "modify_student_data",
edit => "modify_student_data",
savePassword => "change_password",
password => "change_password",
import => "modify_student_data",
export => "modify_classlist_files",
add => "modify_student_data",
delete => "modify_student_data",
};
# permissions needed to view a given field
use constant FIELD_PERMS => {
act_as => "become_student",
sets => "assign_problem_sets",
};
use constant STATE_PARAMS => [qw(user effectiveUser key visible_users no_visible_users prev_visible_users no_prev_visible_users editMode passwordMode primarySortField secondarySortField ternarySortField labelSortMethod)];
use constant SORT_SUBS => {
user_id => \&byUserID,
first_name => \&byFirstName,
last_name => \&byLastName,
email_address => \&byEmailAddress,
student_id => \&byStudentID,
status => \&byStatus,
section => \&bySection,
recitation => \&byRecitation,
comment => \&byComment,
permission => \&byPermission,
};
use constant FIELD_PROPERTIES => {
user_id => {
type => "text",
size => 8,
access => "readonly",
},
first_name => {
type => "text",
size => 10,
access => "readwrite",
},
last_name => {
type => "text",
size => 10,
access => "readwrite",
},
email_address => {
type => "text",
size => 20,
access => "readwrite",
},
student_id => {
type => "text",
size => 11,
access => "readwrite",
},
status => {
#type => "enumerable",
type => "status",
size => 4,
access => "readwrite",
#items => {
# "C" => "Enrolled",
# "D" => "Drop",
# "A" => "Audit",
#},
#synonyms => {
# qr/^[ce]/i => "C",
# qr/^[dw]/i => "D",
# qr/^a/i => "A",
# "*" => "C",
#}
},
section => {
type => "text",
size => 4,
access => "readwrite",
},
recitation => {
type => "text",
size => 4,
access => "readwrite",
},
comment => {
type => "text",
size => 20,
access => "readwrite",
},
permission => {
type => "number",
size => 2,
access => "readwrite",
}
};
sub pre_header_initialize {
my $self = shift;
my $r = $self->r;
my $urlpath = $r->urlpath;
my $authz = $r->authz;
my $ce = $r->ce;
my $courseName = $urlpath->arg("courseID");
my $user = $r->param('user');
# Handle redirects, if any.
##############################
# Redirect to the addUser page
##################################
# Check permissions
return unless $authz->hasPermissions($user, "access_instructor_tools");
defined($r->param('action')) && $r->param('action') eq 'add' && do {
# fix url and redirect
my $root = $ce->{webworkURLs}->{root};
my $numberOfStudents = $r->param('number_of_students');
warn "number of students not defined " unless defined $numberOfStudents;
my $uri=$self->systemLink( $urlpath->newFromModule('WeBWorK::ContentGenerator::Instructor::AddUsers',courseID=>$courseName),
params=>{
number_of_students=>$numberOfStudents,
}
);
#FIXME does the display mode need to be defined?
#FIXME url_authen_args also includes an effective user, so the new one must come first.
# even that might not work with every browser since there are two effective User assignments.
$r->header_out(Location => $uri);
$self->{noContent} = 1; # forces redirect
return;
};
}
# FIXME -- this should be moved up to instructor or contentgenerator
sub header {
my $self = shift;
return REDIRECT if $self->{noContent};
my $r = $self->r;
$r->content_type('text/html');
$r->send_http_header();
return OK;
}
#FIXME -- this should probably be moved up to instructor or contentgenerator as well
#sub nbsp {
# my $str = shift;
# ($str =~/\S/) ? $str : ' ' ; # returns non-breaking space for empty strings
# # tricky cases: $str =0;
# # $str is a complex number
#}
# moved to ContentGenerator.pm
sub initialize {
my ($self) = @_;
my $r = $self->r;
my $db = $r->db;
my $ce = $r->ce;
my $authz = $r->authz;
my $user = $r->param('user');
# Check permissions
return unless $authz->hasPermissions($user, "access_instructor_tools");
#if (defined($r->param('addStudent'))) {
# my $newUser = $db->newUser;
# my $newPermissionLevel = $db->newPermissionLevel;
# my $newPassword = $db->newPassword;
# $newUser->user_id($r->param('newUserID'));
# $newPermissionLevel->user_id($r->param('newUserID'));
# $newPassword->user_id($r->param('newUserID'));
# $newUser->status('C');
# $newPermissionLevel->permission(0);
# $db->addUser($newUser);
# $db->addPermissionLevel($newPermissionLevel);
# $db->addPassword($newPassword);
#}
}
sub body {
my ($self) = @_;
my $r = $self->r;
my $urlpath = $r->urlpath;
my $db = $r->db;
my $ce = $r->ce;
my $authz = $r->authz;
my $courseName = $urlpath->arg("courseID");
my $setID = $urlpath->arg("setID");
my $user = $r->param('user');
my $root = $ce->{webworkURLs}->{root};
# templates for getting field names
my $userTemplate = $self->{userTemplate} = $db->newUser;
my $permissionLevelTemplate = $self->{permissionLevelTemplate} = $db->newPermissionLevel;
return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to access the instructor tools."))
unless $authz->hasPermissions($user, "access_instructor_tools");
# This table can be consulted when display-ready forms of field names are needed.
my %prettyFieldNames = map { $_ => $_ }
$userTemplate->FIELDS(),
$permissionLevelTemplate->FIELDS();
@prettyFieldNames{qw(
user_id
first_name
last_name
email_address
student_id
status
section
recitation
comment
permission
)} = (
"Login Name",
"First Name",
"Last Name",
"Email Address",
"Student ID",
"Status",
"Section",
"Recitation",
"Comment",
"Permission Level"
);
$self->{prettyFieldNames} = \%prettyFieldNames;
########## set initial values for state fields
my @allUserIDs = $db->listUsers;
$self->{totalSets} = $db->listGlobalSets; # save for use in "assigned sets" links
$self->{allUserIDs} = \@allUserIDs;
if (defined $r->param("visable_user_string")) {
my @visableUserIDs = split /:/, $r->param("visable_user_string");
$self->{visibleUserIDs} = [ @visableUserIDs ];
} elsif (defined $r->param("visible_users")) {
$self->{visibleUserIDs} = [ $r->param("visible_users") ];
} elsif (defined $r->param("no_visible_users")) {
$self->{visibleUserIDs} = [];
} else {
if ((@allUserIDs > HIDE_USERS_THRESHHOLD) and (not defined $r->param("show_all_users") )) {
$self->{visibleUserIDs} = [];
} else {
$self->{visibleUserIDs} = [ @allUserIDs ];
}
}
$self->{prevVisibleUserIDs} = $self->{visibleUserIDs};
if (defined $r->param("selected_users")) {
$self->{selectedUserIDs} = [ $r->param("selected_users") ];
} else {
$self->{selectedUserIDs} = [];
}
$self->{editMode} = $r->param("editMode") || 0;
return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify student data"))
if $self->{editMode} and not $authz->hasPermissions($user, "modify_student_data");
$self->{passwordMode} = $r->param("passwordMode") || 0;
return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify student data"))
if $self->{passwordMode} and not $authz->hasPermissions($user, "modify_student_data");
if (defined $r->param("labelSortMethod")) {
$self->{primarySortField} = $r->param("labelSortMethod");
$self->{secondarySortField} = $r->param("primarySortField");
$self->{ternarySortField} = $r->param("secondarySortField");
}
else {
$self->{primarySortField} = $r->param("primarySortField") || "last_name";
$self->{secondarySortField} = $r->param("secondarySortField") || "first_name";
$self->{ternarySortField} = $r->param("ternarySortField") || "student_id";
}
my @allUsers = $db->getUsers(@allUserIDs);
my (%sections, %recitations);
foreach my $User (@allUsers) {
push @{$sections{defined $User->section ? $User->section : ""}}, $User->user_id;
push @{$recitations{defined $User->recitation ? $User->recitation : ""}}, $User->user_id;
}
$self->{sections} = \%sections;
$self->{recitations} = \%recitations;
########## call action handler
my $actionID = $r->param("action");
if ($actionID) {
unless (grep { $_ eq $actionID } @{ VIEW_FORMS() }, @{ EDIT_FORMS() }, @{ PASSWORD_FORMS() } ) {
die "Action $actionID not found";
}
# Check permissions
if (not FORM_PERMS()->{$actionID} or $authz->hasPermissions($user, FORM_PERMS()->{$actionID})) {
my $actionHandler = "${actionID}_handler";
my %genericParams;
foreach my $param (qw(selected_users)) {
$genericParams{$param} = [ $r->param($param) ];
}
my %actionParams = $self->getActionParams($actionID);
my %tableParams = $self->getTableParams();
print CGI::p(
'
',
"Result of last action performed: ",
CGI::i($self->$actionHandler(\%genericParams, \%actionParams, \%tableParams)),
'
',
CGI::hr()
);
} else {
return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to perform this action."));
}
}
########## retrieve possibly changed values for member fields
#@allUserIDs = @{ $self->{allUserIDs} }; # do we need this one?
@allUserIDs = $db->listUsers; # recompute value in case some were added
my @visibleUserIDs = @{ $self->{visibleUserIDs} };
my @prevVisibleUserIDs = @{ $self->{prevVisibleUserIDs} };
my @selectedUserIDs = @{ $self->{selectedUserIDs} };
my $editMode = $self->{editMode};
my $passwordMode = $self->{passwordMode};
my $primarySortField = $self->{primarySortField};
my $secondarySortField = $self->{secondarySortField};
my $ternarySortField = $self->{ternarySortField};
#warn "visibleUserIDs=@visibleUserIDs\n";
#warn "prevVisibleUserIDs=@prevVisibleUserIDs\n";
#warn "selectedUserIDs=@selectedUserIDs\n";
#warn "editMode=$editMode\n";
#warn "passwordMode=$passwordMode\n";
#warn "primarySortField=$primarySortField\n";
#warn "secondarySortField=$secondarySortField\n";
#warn "ternarySortField=$ternarySortField\n";
########## get required users
my @Users = grep { defined $_ } @visibleUserIDs ? $db->getUsers(@visibleUserIDs) : ();
my %sortSubs = %{ SORT_SUBS() };
my $primarySortSub = $sortSubs{$primarySortField};
my $secondarySortSub = $sortSubs{$secondarySortField};
my $ternarySortSub = $sortSubs{$ternarySortField};
# add permission level to user record hash so we can sort it if necessary
if ($primarySortField eq 'permission' or $secondarySortField eq 'permission' or $ternarySortField eq 'permission') {
foreach my $User (@Users) {
next unless $User;
my $permissionLevel = $db->getPermissionLevel($User->user_id);
$User->{permission} = $permissionLevel->permission;
}
}
# # don't forget to sort in opposite order of importance
# @Users = sort $secondarySortSub @Users;
# @Users = sort $primarySortSub @Users;
# #@Users = sort byLnFnUid @Users;
# Always have a definite sort order even if first three sorts don't determine things
@Users = sort {
&$primarySortSub
||
&$secondarySortSub
||
&$ternarySortSub
||
byLastName
||
byFirstName
||
byUserID
}
@Users;
my @PermissionLevels;
for (my $i = 0; $i < @Users; $i++) {
my $User = $Users[$i];
my $PermissionLevel = $db->getPermissionLevel($User->user_id); # checked
unless ($PermissionLevel) {
# uh oh! no permission level record found!
warn "added missing permission level for user ", $User->user_id, "\n";
# create a new permission level record
$PermissionLevel = $db->newPermissionLevel;
$PermissionLevel->user_id($User->user_id);
$PermissionLevel->permission(0);
# add it to the database
$db->addPermissionLevel($PermissionLevel);
}
$PermissionLevels[$i] = $PermissionLevel;
}
########## print beginning of form
print CGI::start_form({method=>"post", action=>$self->systemLink($urlpath,authen=>0), name=>"userlist"});
print $self->hidden_authen_fields();
########## print state data
print "\n\n";
if (@visibleUserIDs) {
print CGI::hidden(-name=>"visible_users", -value=>\@visibleUserIDs);
} else {
print CGI::hidden(-name=>"no_visible_users", -value=>"1");
}
if (@prevVisibleUserIDs) {
print CGI::hidden(-name=>"prev_visible_users", -value=>\@prevVisibleUserIDs);
} else {
print CGI::hidden(-name=>"no_prev_visible_users", -value=>"1");
}
print CGI::hidden(-name=>"editMode", -value=>$editMode);
print CGI::hidden(-name=>"passwordMode", -value=>$passwordMode);
print CGI::hidden(-name=>"primarySortField", -value=>$primarySortField);
print CGI::hidden(-name=>"secondarySortField", -value=>$secondarySortField);
print CGI::hidden(-name=>"ternarySortField", -value=>$ternarySortField);
print "\n\n";
########## print action forms
print CGI::start_table({});
print CGI::Tr({}, CGI::td({-colspan=>2}, "Select an action to perform:"));
my @formsToShow;
if ($editMode) {
@formsToShow = @{ EDIT_FORMS() };
}elsif ($passwordMode) {
@formsToShow = @{ PASSWORD_FORMS() };
} else {
@formsToShow = @{ VIEW_FORMS() };
}
my $i = 0;
foreach my $actionID (@formsToShow) {
# Check permissions
next if FORM_PERMS()->{$actionID} and not $authz->hasPermissions($user, FORM_PERMS()->{$actionID});
my $actionForm = "${actionID}_form";
my $onChange = "document.userlist.action[$i].checked=true";
my %actionParams = $self->getActionParams($actionID);
print CGI::Tr({-valign=>"top"},
CGI::td({}, CGI::input({-type=>"radio", -name=>"action", -value=>$actionID})),
CGI::td({}, $self->$actionForm($onChange, %actionParams))
);
$i++;
}
print CGI::Tr({}, CGI::td({-colspan=>2, -align=>"center"},
CGI::submit(-value=>"Take Action!"))
);
print CGI::end_table();
########## print table
print CGI::p("Showing ", scalar @Users, " out of ", scalar @allUserIDs, " users.");
print CGI::p("If a password field is left blank, the student's current password will be maintained.") if $passwordMode;
if ($editMode) {
print CGI::p('Click on the login name to edit individual problem set data, (e.g. due dates) for these students.');
}
$self->printTableHTML(\@Users, \@PermissionLevels, \%prettyFieldNames,
editMode => $editMode,
passwordMode => $passwordMode,
selectedUserIDs => \@selectedUserIDs,
primarySortField => $primarySortField,
secondarySortField => $secondarySortField,
visableUserIDs => \@visibleUserIDs,
);
########## print end of form
print CGI::end_form();
return "";
}
################################################################################
# extract particular params and put them in a hash (values are ARRAYREFs!)
################################################################################
sub getActionParams {
my ($self, $actionID) = @_;
my $r = $self->{r};
my %actionParams;
foreach my $param ($r->param) {
next unless $param =~ m/^action\.$actionID\./;
$actionParams{$param} = [ $r->param($param) ];
}
return %actionParams;
}
sub getTableParams {
my ($self) = @_;
my $r = $self->{r};
my %tableParams;
foreach my $param ($r->param) {
next unless $param =~ m/^(?:user|permission)\./;
$tableParams{$param} = [ $r->param($param) ];
}
return %tableParams;
}
################################################################################
# actions and action triggers
################################################################################
# filter, edit, cancelEdit, and saveEdit should stay with the display module and
# not be real "actions". that way, all actions are shown in view mode and no
# actions are shown in edit mode.
sub filter_form {
my ($self, $onChange, %actionParams) = @_;
#return CGI::table({}, CGI::Tr({-valign=>"top"},
# CGI::td({},
my %prettyFieldNames = %{ $self->{prettyFieldNames} };
return join("",
"Show ",
CGI::popup_menu(
-name => "action.filter.scope",
-values => [qw(all none selected match_regex)],
-default => $actionParams{"action.filter.scope"}->[0] || "match_regex",
-labels => {
all => "all users",
none => "no users",
selected => "selected users",
# match_ids => "users with matching user IDs:",
match_regex => "users who match:",
# match_section => "users in selected section",
# match_recitation => "users in selected recitation",
},
-onchange => $onChange,
),
" ",
CGI::textfield(
-name => "action.filter.user_ids",
-value => $actionParams{"action.filter.user_ids"}->[0] || "",,
-width => "50",
-onchange => $onChange,
),
# " (separate multiple IDs with commas)",
# CGI::br(),
# "sections: ",
# CGI::popup_menu(
# -name => "action.filter.section",
# -values => [ keys %{ $self->{sections} } ],
# -default => $actionParams{"action.filter.section"}->[0] || "",
# -labels => { $self->menuLabels($self->{sections}) },
# -onchange => $onChange,
# ),
# " recitations: ",
# CGI::popup_menu(
# -name => "action.filter.recitation",
# -values => [ keys %{ $self->{recitations} } ],
# -default => $actionParams{"action.filter.recitation"}->[0] || "",
# -labels => { $self->menuLabels($self->{recitations}) },
# -onchange => $onChange,
# ),
" in their ",
CGI::popup_menu(
-name => "action.filter.field",
-value => [ keys %{ FIELD_PROPERTIES() } ],
-default => $actionParams{"action.filter.field"}->[0] || "user_id",
-labels => \%prettyFieldNames,
-onchange => $onChange,
),
);
# ),
#));
}
# this action handler modifies the "visibleUserIDs" field based on the contents
# of the "action.filter.scope" parameter and the "selected_users"
sub filter_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $r = $self->r;
my $db = $r->db;
my $result;
my $scope = $actionParams->{"action.filter.scope"}->[0];
if ($scope eq "all") {
$result = "showing all users";
$self->{visibleUserIDs} = $self->{allUserIDs};
} elsif ($scope eq "none") {
$result = "showing no users";
$self->{visibleUserIDs} = [];
} elsif ($scope eq "selected") {
$result = "showing selected users";
$self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
} elsif ($scope eq "match_regex") {
$result = "showing matching users";
my $regex = $actionParams->{"action.filter.user_ids"}->[0];
my $field = $actionParams->{"action.filter.field"}->[0];
my @userRecords = $db->getUsers(@{$self->{allUserIDs}});
my @userIDs;
foreach my $record (@userRecords) {
next unless $record;
# add permission level to user record hash so we can match it if necessary
if ($field eq "permission") {
my $permissionLevel = $db->getPermissionLevel($record->user_id);
$record->{permission} = $permissionLevel->permission;
}
push @userIDs, $record->user_id if $record->{$field} =~ /^$regex/i;
}
$self->{visibleUserIDs} = \@userIDs;
} elsif ($scope eq "match_ids") {
my @userIDs = split /\s*,\s*/, $actionParams->{"action.filter.user_ids"}->[0];
$self->{visibleUserIDs} = \@userIDs;
} elsif ($scope eq "match_section") {
my $section = $actionParams->{"action.filter.section"}->[0];
$self->{visibleUserIDs} = $self->{sections}->{$section}; # an arrayref
} elsif ($scope eq "match_recitation") {
my $recitation = $actionParams->{"action.filter.recitation"}->[0];
$self->{visibleUserIDs} = $self->{recitations}->{$recitation}; # an arrayref
}
return $result;
}
sub sort_form {
my ($self, $onChange, %actionParams) = @_;
return join ("",
"Sort by ",
CGI::popup_menu(
-name => "action.sort.primary",
-values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)],
-default => $actionParams{"action.sort.primary"}->[0] || "last_name",
-labels => {
user_id => "Login Name",
first_name => "First Name",
last_name => "Last Name",
email_address => "Email Address",
student_id => "Student ID",
status => "Enrollment Status",
section => "Section",
recitation => "Recitation",
comment => "Comment",
permission => "Permission Level"
},
-onchange => $onChange,
),
", then by ",
CGI::popup_menu(
-name => "action.sort.secondary",
-values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)],
-default => $actionParams{"action.sort.secondary"}->[0] || "first_name",
-labels => {
user_id => "Login Name",
first_name => "First Name",
last_name => "Last Name",
email_address => "Email Address",
student_id => "Student ID",
status => "Enrollment Status",
section => "Section",
recitation => "Recitation",
comment => "Comment",
permission => "Permission Level"
},
-onchange => $onChange,
),
", then by ",
CGI::popup_menu(
-name => "action.sort.ternary",
-values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)],
-default => $actionParams{"action.sort.ternary"}->[0] || "user_id",
-labels => {
user_id => "Login Name",
first_name => "First Name",
last_name => "Last Name",
email_address => "Email Address",
student_id => "Student ID",
status => "Enrollment Status",
section => "Section",
recitation => "Recitation",
comment => "Comment",
permission => "Permission Level"
},
-onchange => $onChange,
),
".",
);
}
sub sort_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $primary = $actionParams->{"action.sort.primary"}->[0];
my $secondary = $actionParams->{"action.sort.secondary"}->[0];
my $ternary = $actionParams->{"action.sort.ternary"}->[0];
$self->{primarySortField} = $primary;
$self->{secondarySortField} = $secondary;
$self->{ternarySortField} = $ternary;
my %names = (
user_id => "Login Name",
first_name => "First Name",
last_name => "Last Name",
email_address => "Email Address",
student_id => "Student ID",
status => "Enrollment Status",
section => "Section",
recitation => "Recitation",
comment => "Comment",
permission => "Permission Level"
);
return "Users sorted by $names{$primary}, then by $names{$secondary}, then by $names{$ternary}.";
}
sub edit_form {
my ($self, $onChange, %actionParams) = @_;
return join("",
"Edit ",
CGI::popup_menu(
-name => "action.edit.scope",
-values => [qw(all visible selected)],
-default => $actionParams{"action.edit.scope"}->[0] || "selected",
-labels => {
all => "all users",
visible => "visible users",
selected => "selected users"
},
-onchange => $onChange,
),
);
}
sub edit_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $result;
my $scope = $actionParams->{"action.edit.scope"}->[0];
if ($scope eq "all") {
$result = "editing all users";
$self->{visibleUserIDs} = $self->{allUserIDs};
} elsif ($scope eq "visible") {
$result = "editing visible users";
# leave visibleUserIDs alone
} elsif ($scope eq "selected") {
$result = "editing selected users";
$self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
}
$self->{editMode} = 1;
return $result;
}
sub password_form {
my ($self, $onChange, %actionParams) = @_;
return join("",
"Give new password to ",
CGI::popup_menu(
-name => "action.password.scope",
-values => [qw(all visible selected)],
-default => $actionParams{"action.password.scope"}->[0] || "selected",
-labels => {
all => "all users",
visible => "visible users",
selected => "selected users"
},
-onchange => $onChange,
),
);
}
sub password_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $result;
my $scope = $actionParams->{"action.password.scope"}->[0];
if ($scope eq "all") {
$result = "giving new passwords to all users";
$self->{visibleUserIDs} = $self->{allUserIDs};
} elsif ($scope eq "visible") {
$result = "giving new passwords to visible users";
# leave visibleUserIDs alone
} elsif ($scope eq "selected") {
$result = "giving new passwords to selected users";
$self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
}
$self->{passwordMode} = 1;
return $result;
}
sub delete_form {
my ($self, $onChange, %actionParams) = @_;
return join("",
CGI::div({class=>"ResultsWithError"},
"Delete ",
CGI::popup_menu(
-name => "action.delete.scope",
-values => [qw(none selected)],
-default => $actionParams{"action.delete.scope"}->[0] || "none",
-labels => {
none => "no users.",
#visible => "visible users.",
selected => "selected users."
},
-onchange => $onChange,
),
CGI::em(" Deletion destroys all user-related data and is not undoable!"),
),
);
}
sub delete_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $r = $self->r;
my $db = $r->db;
my $user = $r->param('user');
my $scope = $actionParams->{"action.delete.scope"}->[0];
my @userIDsToDelete = ();
#if ($scope eq "visible") {
# @userIDsToDelete = @{ $self->{visibleUserIDs} };
#} elsif ($scope eq "selected") {
if ($scope eq "selected") {
@userIDsToDelete = @{ $self->{selectedUserIDs} };
}
my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
my %visibleUserIDs = map { $_ => 1 } @{ $self->{visibleUserIDs} };
my %selectedUserIDs = map { $_ => 1 } @{ $self->{selectedUserIDs} };
my $error = "";
my $num = 0;
foreach my $userID (@userIDsToDelete) {
if ($user eq $userID) { # don't delete yourself!!
$error = "You cannot delete yourself!";
next;
}
delete $allUserIDs{$userID};
delete $visibleUserIDs{$userID};
delete $selectedUserIDs{$userID};
$db->deleteUser($userID);
$num++;
}
$self->{allUserIDs} = [ keys %allUserIDs ];
$self->{visibleUserIDs} = [ keys %visibleUserIDs ];
$self->{selectedUserIDs} = [ keys %selectedUserIDs ];
return "deleted $num user" . ($num == 1 ? "" : "s. ") . $error;
}
sub add_form {
my ($self, $onChange, %actionParams) = @_;
return "Add ", CGI::input({name=>'number_of_students', value=>1,size => 3}), " student(s). ";
}
sub add_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
# This action is redirected to the addUser.pm module using ../instructor/add_user/...
return "Nothing done by add student handler";
}
sub import_form {
my ($self, $onChange, %actionParams) = @_;
return join(" ",
"Import users from file",
CGI::popup_menu(
-name => "action.import.source",
-values => [ $self->getCSVList() ],
-default => $actionParams{"action.import.source"}->[0] || "",
-onchange => $onChange,
),
"replacing",
CGI::popup_menu(
-name => "action.import.replace",
-values => [qw(any visible selected none)],
-default => $actionParams{"action.import.replace"}->[0] || "none",
-labels => {
any => "any",
visible => "visible",
selected => "selected",
none => "no",
},
-onchange => $onChange,
),
"existing users and adding",
CGI::popup_menu(
-name => "action.import.add",
-values => [qw(any none)],
-default => $actionParams{"action.import.add"}->[0] || "any",
-labels => {
any => "any",
none => "no",
},
-onchange => $onChange,
),
"new users",
);
}
sub import_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $source = $actionParams->{"action.import.source"}->[0];
my $add = $actionParams->{"action.import.add"}->[0];
my $replace = $actionParams->{"action.import.replace"}->[0];
my $fileName = $source;
my $createNew = $add eq "any";
my $replaceExisting;
my @replaceList;
if ($replace eq "any") {
$replaceExisting = "any";
} elsif ($replace eq "none") {
$replaceExisting = "none";
} elsif ($replace eq "visible") {
$replaceExisting = "listed";
@replaceList = @{ $self->{visibleUserIDs} };
} elsif ($replace eq "selected") {
$replaceExisting = "listed";
@replaceList = @{ $self->{selectedUserIDs} };
}
my ($replaced, $added, $skipped)
= $self->importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList);
# make new users visible... do we really want to do this? probably.
push @{ $self->{visibleUserIDs} }, @$added;
my $numReplaced = @$replaced;
my $numAdded = @$added;
my $numSkipped = @$skipped;
return $numReplaced . " user" . ($numReplaced == 1 ? "" : "s") . " replaced, "
. $numAdded . " user" . ($numAdded == 1 ? "" : "s") . " added, "
. $numSkipped . " user" . ($numSkipped == 1 ? "" : "s") . " skipped"
. " (" . join (", ", @$skipped) . ") ";
}
sub export_form {
my ($self, $onChange, %actionParams) = @_;
return join("",
"Export ",
CGI::popup_menu(
-name => "action.export.scope",
-values => [qw(all visible selected)],
-default => $actionParams{"action.export.scope"}->[0] || "visible",
-labels => {
all => "all users",
visible => "visible users",
selected => "selected users"
},
-onchange => $onChange,
),
" to ",
CGI::popup_menu(
-name=>"action.export.target",
-values => [ "new", $self->getCSVList() ],
-labels => { new => "a new file named:" },
-default => $actionParams{"action.export.target"}->[0] || "",
-onchange => $onChange,
),
#CGI::br(),
#"new file to create: ",
CGI::textfield(
-name => "action.export.new",
-value => $actionParams{"action.export.new"}->[0] || "",,
-width => "50",
-onchange => $onChange,
),
CGI::tt(".lst"),
);
}
sub export_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $r = $self->r;
my $ce = $r->ce;
my $dir = $ce->{courseDirs}->{templates};
my $scope = $actionParams->{"action.export.scope"}->[0];
my $target = $actionParams->{"action.export.target"}->[0];
my $new = $actionParams->{"action.export.new"}->[0];
#get name of templates directory as it appears in file manager
$dir =~ s|.*/||;
my $fileName;
if ($target eq "new") {
$fileName = $new;
} else {
$fileName = $target;
}
$fileName .= ".lst" unless $fileName =~ m/\.lst$/;
my @userIDsToExport;
if ($scope eq "all") {
@userIDsToExport = @{ $self->{allUserIDs} };
} elsif ($scope eq "visible") {
@userIDsToExport = @{ $self->{visibleUserIDs} };
} elsif ($scope eq "selected") {
@userIDsToExport = @{ $self->{selectedUserIDs} };
}
$self->exportUsersToCSV($fileName, @userIDsToExport);
return scalar @userIDsToExport . " users exported to file $dir/$fileName";
}
sub cancelEdit_form {
my ($self, $onChange, %actionParams) = @_;
return "Abandon changes";
}
sub cancelEdit_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $r = $self->r;
#$self->{selectedUserIDs} = $self->{visibleUserIDs};
# only do the above if we arrived here via "edit selected users"
if (defined $r->param("prev_visible_users")) {
$self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
} elsif (defined $r->param("no_prev_visible_users")) {
$self->{visibleUserIDs} = [];
} else {
# leave it alone
}
$self->{editMode} = 0;
return "changes abandoned";
}
sub saveEdit_form {
my ($self, $onChange, %actionParams) = @_;
return "Save changes";
}
sub saveEdit_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $r = $self->r;
my $db = $r->db;
my @visibleUserIDs = @{ $self->{visibleUserIDs} };
foreach my $userID (@visibleUserIDs) {
my $User = $db->getUser($userID); # checked
die "record for visible user $userID not found" unless $User;
my $PermissionLevel = $db->getPermissionLevel($userID); # checked
die "permissions for $userID not defined" unless defined $PermissionLevel;
foreach my $field ($User->NONKEYFIELDS()) {
my $param = "user.${userID}.${field}";
if (defined $tableParams->{$param}->[0]) {
$User->$field($tableParams->{$param}->[0]);
}
}
foreach my $field ($PermissionLevel->NONKEYFIELDS()) {
my $param = "permission.${userID}.${field}";
if (defined $tableParams->{$param}->[0]) {
$PermissionLevel->$field($tableParams->{$param}->[0]);
}
}
$db->putUser($User);
$db->putPermissionLevel($PermissionLevel);
}
if (defined $r->param("prev_visible_users")) {
$self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
} elsif (defined $r->param("no_prev_visible_users")) {
$self->{visibleUserIDs} = [];
} else {
# leave it alone
}
$self->{editMode} = 0;
return "changes saved";
}
sub cancelPassword_form {
my ($self, $onChange, %actionParams) = @_;
return "Abandon changes";
}
sub cancelPassword_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $r = $self->r;
#$self->{selectedUserIDs} = $self->{visibleUserIDs};
# only do the above if we arrived here via "edit selected users"
if (defined $r->param("prev_visible_users")) {
$self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
} elsif (defined $r->param("no_prev_visible_users")) {
$self->{visibleUserIDs} = [];
} else {
# leave it alone
}
$self->{passwordMode} = 0;
return "changes abandoned";
}
sub savePassword_form {
my ($self, $onChange, %actionParams) = @_;
return "Save changes";
}
sub savePassword_handler {
my ($self, $genericParams, $actionParams, $tableParams) = @_;
my $r = $self->r;
my $db = $r->db;
my @visibleUserIDs = @{ $self->{visibleUserIDs} };
foreach my $userID (@visibleUserIDs) {
my $User = $db->getUser($userID); # checked
die "record for visible user $userID not found" unless $User;
my $param = "user.${userID}.new_password";
if ((defined $tableParams->{$param}->[0]) and ($tableParams->{$param}->[0])) {
my $newP = $tableParams->{$param}->[0];
my $Password = eval {$db->getPassword($User->user_id)}; # checked
my $cryptPassword = cryptPassword($newP);
$Password->password(cryptPassword($newP));
eval { $db->putPassword($Password) };
}
}
if (defined $r->param("prev_visible_users")) {
$self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
} elsif (defined $r->param("no_prev_visible_users")) {
$self->{visibleUserIDs} = [];
} else {
# leave it alone
}
$self->{passwordMode} = 0;
return "new passwords saved";
}
################################################################################
# sorts
################################################################################
sub byUserID { lc $a->user_id cmp lc $b->user_id }
sub byFirstName { (defined $a->first_name && defined $b->first_name) ? lc $a->first_name cmp lc $b->first_name : 0; }
sub byLastName { (defined $a->last_name && defined $b->last_name ) ? lc $a->last_name cmp lc $b->last_name : 0; }
sub byEmailAddress { lc $a->email_address cmp lc $b->email_address }
sub byStudentID { lc $a->student_id cmp lc $b->student_id }
sub byStatus { lc $a->status cmp lc $b->status }
sub bySection { lc $a->section cmp lc $b->section }
sub byRecitation { lc $a->recitation cmp lc $b->recitation }
sub byComment { lc $a->comment cmp lc $b->comment }
sub byPermission { $a->{permission} <=> $b->{permission} } ## permission level is added to user record hash so we can sort it if necessary
# sub byLnFnUid { &byLastName || &byFirstName || &byUserID }
################################################################################
# utilities
################################################################################
# generate labels for section/recitation popup menus
sub menuLabels {
my ($self, $hashRef) = @_;
my %hash = %$hashRef;
my %result;
foreach my $key (keys %hash) {
my $count = @{ $hash{$key} };
my $displayKey = $key || "";
$result{$key} = "$displayKey ($count users)";
}
return %result;
}
sub importUsersFromCSV {
my ($self, $fileName, $createNew, $replaceExisting, @replaceList) = @_;
my $r = $self->r;
my $ce = $r->ce;
my $db = $r->db;
my $dir = $ce->{courseDirs}->{templates};
my $user = $r->param('user');
die "illegal character in input: '/'" if $fileName =~ m|/|;
die "won't be able to read from file $dir/$fileName: does it exist? is it readable?"
unless -r "$dir/$fileName";
my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
my %replaceOK;
if ($replaceExisting eq "none") {
%replaceOK = ();
} elsif ($replaceExisting eq "listed") {
%replaceOK = map { $_ => 1 } @replaceList;
} elsif ($replaceExisting eq "any") {
%replaceOK = %allUserIDs;
}
my $default_permission_level = $ce->{default_permission_level};
my (@replaced, @added, @skipped);
# get list of hashrefs representing lines in classlist file
my @classlist = parse_classlist("$dir/$fileName");
foreach my $record (@classlist) {
my %record = %$record;
my $user_id = $record{user_id};
if ($user_id eq $user) { # don't replace yourself!!
push @skipped, $user_id;
next;
}
if (exists $allUserIDs{$user_id} and not exists $replaceOK{$user_id}) {
push @skipped, $user_id;
next;
}
if (not exists $allUserIDs{$user_id} and not $createNew) {
push @skipped, $user_id;
next;
}
my $User = $db->newUser(%record);
my $PermissionLevel = $db->newPermissionLevel(user_id => $user_id, permission => 0);
my $Password = $db->newPassword(user_id => $user_id, password => cryptPassword($record{student_id}));
# use password and permission from record if there
if (exists $record{permission}) {
# make sure permission level is numeric
unless (defined($record{permission}) and $record{permission} =~ m/^[+\-]?\d*$/) {
$self->addbadmessage("permission levelĘ for user '$user_id' is not defined or is not an integer. Set the permission level to the default permission level '$default_permission_level'.\n");
$record{permission} = $default_permission_level;
}
$PermissionLevel->permission($record{permission});
}
if (exists $record{password}) {
$Password->password($record{password});
}
if (exists $allUserIDs{$user_id}) {
$db->putUser($User);
$db->putPermissionLevel($PermissionLevel);
$db->putPassword($Password);
push @replaced, $user_id;
} else {
$db->addUser($User);
$db->addPermissionLevel($PermissionLevel);
$db->addPassword($Password);
push @added, $user_id;
}
}
return \@replaced, \@added, \@skipped;
}
sub exportUsersToCSV {
my ($self, $fileName, @userIDsToExport) = @_;
my $r = $self->r;
my $ce = $r->ce;
my $db = $r->db;
my $dir = $ce->{courseDirs}->{templates};
die "illegal character in input: '/'" if $fileName =~ m|/|;
my @records;
my @Users = $db->getUsers(@userIDsToExport);
my @Passwords = $db->getPasswords(@userIDsToExport);
my @PermissionLevels = $db->getPermissionLevels(@userIDsToExport);
foreach my $i (0 .. $#userIDsToExport) {
my $User = $Users[$i];
my $Password = $Passwords[$i];
my $PermissionLevel = $PermissionLevels[$i];
next unless defined $User;
my %record = (
defined $PermissionLevel ? $PermissionLevel->toHash : (),
defined $Password ? $Password->toHash : (),
$User->toHash,
);
push @records, \%record;
}
write_classlist("$dir/$fileName", @records);
}
################################################################################
# "display" methods
################################################################################
sub fieldEditHTML {
my ($self, $fieldName, $value, $properties) = @_;
my $ce = $self->r->ce;
my $size = $properties->{size};
my $type = $properties->{type};
my $access = $properties->{access};
my $items = $properties->{items};
my $synonyms = $properties->{synonyms};
if ($type eq "email") {
if ($value eq ' ') {
return $value;}
else {
return CGI::a({-href=>"mailto:$value"},$value);
}
}
if ($access eq "readonly") {
# hack for status
if ($type eq "status") {
my $status_name = $ce->status_abbrev_to_name($value);
if (defined $status_name) {
$value = "$status_name ($value)";
}
}
return $value;
}
if ($type eq "number" or $type eq "text") {
return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size});
}
if ($type eq "enumerable") {
my $matched = undef; # Whether a synonym match has occurred
# Process synonyms for enumerable objects
foreach my $synonym (keys %$synonyms) {
if ($synonym ne "*" and $value =~ m/$synonym/) {
$value = $synonyms->{$synonym};
$matched = 1;
}
}
if (!$matched and exists $synonyms->{"*"}) {
$value = $synonyms->{"*"};
}
return CGI::popup_menu({
name => $fieldName,
values => [keys %$items],
default => $value,
labels => $items,
});
}
if ($type eq "status") {
# we used to surreptitously map synonyms to a canonical value...
# so should we continue to do that?
my $status_name = $ce->status_abbrev_to_name($value);
if (defined $status_name) {
$value = ($ce->status_name_to_abbrevs($status_name))[0];
}
my (@values, %labels);
while (my ($k, $v) = each %{$ce->{statuses}}) {
my @abbrevs = @{$v->{abbrevs}};
push @values, $abbrevs[0];
foreach my $abbrev (@abbrevs) {
$labels{$abbrev} = $k;
}
}
return CGI::popup_menu({
name => $fieldName,
values => \@values,
default => $value,
labels => \%labels,
});
}
}
sub recordEditHTML {
my ($self, $User, $PermissionLevel, %options) = @_;
my $r = $self->r;
my $urlpath = $r->urlpath;
my $db = $r->db;
my $ce = $r->ce;
my $authz = $r->authz;
my $user = $r->param('user');
my $root = $ce->{webworkURLs}->{root};
my $courseName = $urlpath->arg("courseID");
my $editMode = $options{editMode};
my $passwordMode = $options{passwordMode};
my $userSelected = $options{userSelected};
my $statusClass = $ce->status_abbrev_to_name($User->status);
my $sets = $db->countUserSets($User->user_id);
my $totalSets = $self->{totalSets};
my $changeEUserURL = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}),
params => {effectiveUser => $User->user_id}
);
my $setsAssignedToUserURL = $self->systemLink($urlpath->new(type=>'instructor_user_detail',
args=>{courseID => $courseName,
userID => $User->user_id
}),
params => {effectiveUser => $User->user_id}
);
my $userListURL = $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName} )) . "&editMode=1&visible_users=" . $User->user_id;
my $imageURL = $ce->{webworkURLs}->{htdocs}."/images/edit.gif";
my $imageLink = CGI::a({href => $userListURL}, CGI::img({src=>$imageURL, border=>0}));
my @tableCells;
# Select
if ($editMode or $passwordMode) {
# column not there
} else {
# selection checkbox
push @tableCells, CGI::checkbox(
-name => "selected_users",
-value => $User->user_id,
-checked => $userSelected,
-label => "",
);
}
# Act As
if ($editMode or $passwordMode) {
# column not there
} else {
# selection checkbox
if ( FIELD_PERMS()->{act_as} and not $authz->hasPermissions($user, FIELD_PERMS()->{act_as}) ){
push @tableCells, $User->user_id . $imageLink;
} else {
push @tableCells, CGI::a({href=>$changeEUserURL}, $User->user_id) . $imageLink;
}
}
# Login Status
if ($editMode or $passwordMode) {
# column not there
} else {
# check to see if a user is currently logged in
my $Key = $db->getKey($User->user_id);
push @tableCells, ($Key and WeBWorK::Authen::checkKey($self, $User->user_id, $Key->key)) ? CGI::b("active") : CGI::em("inactive");
}
# change password (only in password mode)
if ($passwordMode) {
if ($User->user_id eq $user) {
push @tableCells, '' # don't allow a professor to change their own password from this form
}
else {
my $fieldName = 'user.' . $User->user_id . '.' . 'new_password';
push @tableCells, CGI::input({type=>"text", name=>$fieldName, size=>14});;
}
}
# User ID (edit mode) or Assigned Sets (otherwise)
if ( $passwordMode) {
# straight user ID
push @tableCells, CGI::div({class=>$statusClass}, $User->user_id);
} elsif ($editMode) {
# straight user ID
my $userDetailPage = $urlpath->new(type =>'instructor_user_detail',
args =>{
courseID => $courseName,
userID => $User->user_id, #FIXME eventually this should be a list??
}
);
my $userDetailUrl = $self->systemLink($userDetailPage,params =>{});
push @tableCells, CGI::a({href=>$userDetailUrl}, $User->user_id);
} else {
# "edit sets assigned to user" link
#push @tableCells, CGI::a({href=>$setsAssignedToUserURL}, "Edit sets");
if ( FIELD_PERMS()->{sets} and not $authz->hasPermissions($user, FIELD_PERMS()->{sets}) ) {
push @tableCells, "$sets/$totalSets";
} else {
push @tableCells, CGI::a({href=>$setsAssignedToUserURL}, "$sets/$totalSets");
}
}
# User Fields
foreach my $field ($User->NONKEYFIELDS) {
my $fieldName = 'user.' . $User->user_id . '.' . $field,
my $fieldValue = $User->$field;
my %properties = %{ FIELD_PROPERTIES()->{$field} };
$properties{access} = 'readonly' unless $editMode;
$properties{type} = 'email' if ($field eq 'email_address' and !$editMode and !$passwordMode);
$fieldValue = $self->nbsp($fieldValue) unless $editMode;
push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties));
}
# PermissionLevel Fields
foreach my $field ($PermissionLevel->NONKEYFIELDS) {
my $fieldName = 'permission.' . $PermissionLevel->user_id . '.' . $field,
my $fieldValue = $PermissionLevel->$field;
my %properties = %{ FIELD_PROPERTIES()->{$field} };
$properties{access} = 'readonly' unless $editMode;
$fieldValue = $self->nbsp($fieldValue) unless $editMode;
push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties));
}
return CGI::Tr({}, CGI::td({nowrap=>1}, \@tableCells));
}
sub printTableHTML {
my ($self, $UsersRef, $PermissionLevelsRef, $fieldNamesRef, %options) = @_;
my $r = $self->r;
my $urlpath = $r->urlpath;
my $courseName = $urlpath->arg("courseID");
my $userTemplate = $self->{userTemplate};
my $permissionLevelTemplate = $self->{permissionLevelTemplate};
my @Users = @$UsersRef;
my @PermissionLevels = @$PermissionLevelsRef;
my %fieldNames = %$fieldNamesRef;
my $editMode = $options{editMode};
my $passwordMode = $options{passwordMode};
my %selectedUserIDs = map { $_ => 1 } @{ $options{selectedUserIDs} };
# my $currentSort = $options{currentSort};
my $primarySortField = $options{primarySortField};
my $secondarySortField = $options{secondarySortField};
my @visableUserIDs = @{ $options{visableUserIDs} };
# names of headings:
my @realFieldNames = (
$userTemplate->KEYFIELDS,
$userTemplate->NONKEYFIELDS,
$permissionLevelTemplate->NONKEYFIELDS,
);
# my %sortSubs = %{ SORT_SUBS() };
#my @stateParams = @{ STATE_PARAMS() };
#my $hrefPrefix = $r->uri . "?" . $self->url_args(@stateParams); # $self->url_authen_args
my @tableHeadings;
foreach my $field (@realFieldNames) {
my $result = $fieldNames{$field};
push @tableHeadings, $result;
};
# prepend selection checkbox? only if we're NOT editing!
unless($editMode or $passwordMode) {
#warn "line 1582 visibleUserIDs=@visableUserIDs \n";
my %current_state =();
if (@visableUserIDs) {
# This is a hack to get around: Maximum URL Length Is 2,083 Characters in Internet Explorer.
# Without passing visable users the URL is about 250 characters. If the total URL is under the limit
# we will pass visable users. If it is over, we will not pass any and all users will be displayed.
# Maybe we should replace the GET method by POST (but this doesn't look good) --- AKP
my $visableUserIDsString = join ':', @visableUserIDs;
if (length($visableUserIDsString) < 1830) {
%current_state = (
primarySortField => "$primarySortField",
secondarySortField => "$secondarySortField",
visable_user_string => "$visableUserIDsString"
);
} else {
%current_state = (
primarySortField => "$primarySortField",
secondarySortField => "$secondarySortField",
show_all_users => "1"
);
}
} else {
%current_state = (
primarySortField => "$primarySortField",
secondarySortField => "$secondarySortField",
no_visible_users => "1"
);
}
@tableHeadings = (
"Select",
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'user_id', %current_state})}, 'Login Name'),
"Login Status",
"Assigned Sets",
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'first_name', %current_state})}, 'First Name'),
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'last_name', %current_state})}, 'Last Name'),
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'email_address', %current_state})}, 'Email Address'),
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'student_id', %current_state})}, 'Student ID'),
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'status', %current_state})}, 'Status'),
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'section', %current_state})}, 'Section'),
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'recitation', %current_state})}, 'Recitation'),
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'comment', %current_state})}, 'Comment'),
CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'permission', %current_state})}, 'Permission Level'),
)
}
if($passwordMode) {
unshift @tableHeadings, "New Password";
}
# print the table
if ($editMode or $passwordMode) {
print CGI::start_table({});
} else {
print CGI::start_table({-border=>1, -nowrap=>1});
}
print CGI::Tr({}, CGI::th({}, \@tableHeadings));
for (my $i = 0; $i < @Users; $i++) {
my $User = $Users[$i];
my $PermissionLevel = $PermissionLevels[$i];
print $self->recordEditHTML($User, $PermissionLevel,
editMode => $editMode,
passwordMode => $passwordMode,
userSelected => exists $selectedUserIDs{$User->user_id}
);
}
print CGI::end_table();
#########################################
# if there are no users shown print message
#
##########################################
print CGI::p(
CGI::i("No students shown. Choose one of the options above to
list the students in the course.")
) unless @Users;
}
1;