[system] / trunk / webwork-modperl / lib / WeBWorK / ContentGenerator / Instructor / UserList.pm Repository:
ViewVC logotype

View of /trunk/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1663 - (download) (as text) (annotate)
Tue Dec 9 01:12:32 2003 UTC (9 years, 5 months ago) by sh002i
File size: 38459 byte(s)
Normalized headers. All files now contain the text below as a header.
This is important since all files now (a) use the full name of the
package, (b) assign copyright to "The WeBWorK Project", (c) give the
full path of the file (relative to CVSROOT) instead of simply the file
name, and (d) include license and warranty information.

Here is the new header:

################################################################################
# WeBWorK Online Homework Delivery System
# Copyright © 2000-2003 The WeBWorK Projcct, http://openwebwork.sf.net/
# $CVSHeader$
#
# 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.
################################################################################

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader$
    5 #
    6 # This program is free software; you can redistribute it and/or modify it under
    7 # the terms of either: (a) the GNU General Public License as published by the
    8 # Free Software Foundation; either version 2, or (at your option) any later
    9 # version, or (b) the "Artistic License" which comes with this package.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 package WeBWorK::ContentGenerator::Instructor::UserList;
   18 use base qw(WeBWorK::ContentGenerator::Instructor);
   19 
   20 =head1 NAME
   21 
   22 WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific
   23 data editing
   24 
   25 =cut
   26 
   27 =for comment
   28 
   29 What do we want to be able to do here?
   30 
   31 Filter what users are shown:
   32   - none, all, selected
   33   - matching user_id, matching section, matching recitation
   34 Switch from view mode to edit mode:
   35   - showing visible users
   36   - showing selected users
   37 Switch from edit mode to view and save changes
   38 Switch from edit mode to view and abandon changes
   39 Delete users:
   40   - visible
   41   - selected
   42 Import users:
   43   - replace:
   44     - any users
   45     - visible users
   46     - selected users
   47     - no users
   48   - add:
   49     - any users
   50     - no users
   51 Export users:
   52   - export:
   53     - all
   54     - visible
   55     - selected
   56   - to:
   57     - existing file on server (overwrite): [ list of files ]
   58     - new file on server (create): [ filename ]
   59 
   60 =cut
   61 
   62 use strict;
   63 use warnings;
   64 use CGI qw();
   65 use WeBWorK::Utils qw(readFile readDirectory);
   66 
   67 use constant HIDE_USERS_THRESHHOLD => 20;
   68 use constant EDIT_FORMS => [qw(cancelEdit saveEdit)];
   69 use constant VIEW_FORMS => [qw(filter edit delete import export)];
   70 use constant STATE_PARAMS => [qw(user effectiveUser key visible_users no_visible_users prev_visible_users no_prev_visible_users editMode sortField)];
   71 
   72 use constant SORT_SUBS => {
   73   user_id       => \&byUserID,
   74   first_name    => \&byFirstName,
   75   last_name     => \&byLastName,
   76   email_address => \&byEmailAddress,
   77   student_id    => \&byStudentID,
   78   status        => \&byStatus,
   79   section       => \&bySection,
   80   recitation    => \&byRecitation,
   81   comment       => \&byComment,
   82 };
   83 
   84 use constant  FIELD_PROPERTIES => {
   85   user_id => {
   86     type => "text",
   87     size => 8,
   88     access => "readonly",
   89   },
   90   first_name => {
   91     type => "text",
   92     size => 10,
   93     access => "readwrite",
   94   },
   95   last_name => {
   96     type => "text",
   97     size => 10,
   98     access => "readwrite",
   99   },
  100   email_address => {
  101     type => "text",
  102     size => 20,
  103     access => "readwrite",
  104   },
  105   student_id => {
  106     type => "text",
  107     size => 11,
  108     access => "readwrite",
  109   },
  110   status => {
  111     type => "enumerable",
  112     size => 4,
  113     access => "readwrite",
  114     items => {
  115       "C" => "Enrolled",
  116       "D" => "Drop",
  117       "A" => "Audit",
  118     },
  119     synonyms => {
  120       qr/^[ce]/i => "C",
  121       qr/^[dw]/i => "D",
  122       qr/^a/i => "A",
  123       "*" => "C",
  124     }
  125   },
  126   section => {
  127     type => "text",
  128     size => 4,
  129     access => "readwrite",
  130   },
  131   recitation => {
  132     type => "text",
  133     size => 4,
  134     access => "readwrite",
  135   },
  136   comment => {
  137     type => "text",
  138     size => 20,
  139     access => "readwrite",
  140   },
  141   permission => {
  142     type => "number",
  143     size => 2,
  144     access => "readwrite",
  145   }
  146 };
  147 
  148 sub initialize {
  149   my ($self) = @_;
  150   my $r = $self->{r};
  151   my $db = $self->{db};
  152   my $ce = $self->{ce};
  153   my $authz = $self->{authz};
  154   my $user = $r->param('user');
  155 
  156   unless ($authz->hasPermissions($user, "modify_student_data")) {
  157     $self->{submitError} = "You are not authorized to modify student data";
  158     return;
  159   }
  160 
  161   #if (defined($r->param('addStudent'))) {
  162   # my $newUser = $db->newUser;
  163   # my $newPermissionLevel = $db->newPermissionLevel;
  164   # my $newPassword = $db->newPassword;
  165   # $newUser->user_id($r->param('newUserID'));
  166   # $newPermissionLevel->user_id($r->param('newUserID'));
  167   # $newPassword->user_id($r->param('newUserID'));
  168   # $newUser->status('C');
  169   # $newPermissionLevel->permission(0);
  170   # $db->addUser($newUser);
  171   # $db->addPermissionLevel($newPermissionLevel);
  172   # $db->addPassword($newPassword);
  173   #}
  174 }
  175 
  176 sub title {
  177   my $self = shift;
  178   return "User List";
  179 }
  180 
  181 sub path {
  182   my $self = shift;
  183   my $args = $_[-1];
  184   my $ce = $self->{ce};
  185   my $root = $ce->{webworkURLs}->{root};
  186   my $courseName = $ce->{courseName};
  187 
  188   return $self->pathMacro($args,
  189     "Home"              => "$root",
  190     $courseName         => "$root/$courseName",
  191     "Instructor Tools"  => "$root/$courseName/instructor",
  192     "Users"             => "", # "$root/$courseName/instructor/users",
  193   );
  194 }
  195 
  196 sub body {
  197   my ($self, $setID) = @_;
  198   my $r = $self->{r};
  199   my $authz = $self->{authz};
  200   my $user = $r->param('user');
  201   my $db = $self->{db};
  202   my $ce = $self->{ce};
  203   my $root = $ce->{webworkURLs}->{root};
  204   my $courseName = $ce->{courseName};
  205 
  206   # templates for getting field names
  207   my $userTemplate            = $self->{userTemplate}            = $db->newUser;
  208   my $permissionLevelTemplate = $self->{permissionLevelTemplate} = $db->newPermissionLevel;
  209 
  210   return CGI::em("You are not authorized to access the Instructor tools.")
  211     unless $authz->hasPermissions($user, "access_instructor_tools");
  212 
  213   # This table can be consulted when display-ready forms of field names are needed.
  214   my %prettyFieldNames = map { $_ => $_ }
  215     $userTemplate->FIELDS(),
  216     $permissionLevelTemplate->FIELDS();
  217 
  218   @prettyFieldNames{qw(
  219     user_id
  220     first_name
  221     last_name
  222     email_address
  223     student_id
  224     status
  225     section
  226     recitation
  227     comment
  228     permission
  229   )} = (
  230     "User ID",
  231     "First Name",
  232     "Last Name",
  233     "E-mail",
  234     "Student ID",
  235     "Status",
  236     "Section",
  237     "Recitation",
  238     "Comment",
  239     "Perm. Level"
  240   );
  241 
  242   ########## set initial values for state fields
  243 
  244   my @allUserIDs = $db->listUsers;
  245   $self->{allUserIDs} = \@allUserIDs;
  246 
  247   if (defined $r->param("visible_users")) {
  248     $self->{visibleUserIDs} = [ $r->param("visible_users") ];
  249   } elsif (defined $r->param("no_visible_users")) {
  250     $self->{visibleUserIDs} = [];
  251   } else {
  252     if (@allUserIDs > HIDE_USERS_THRESHHOLD) {
  253       $self->{visibleUserIDs} = [];
  254     } else {
  255       $self->{visibleUserIDs} = [ @allUserIDs ];
  256     }
  257   }
  258 
  259   $self->{prevVisibleUserIDs} = $self->{visibleUserIDs};
  260 
  261   if (defined $r->param("selected_users")) {
  262     $self->{selectedUserIDs} = [ $r->param("selected_users") ];
  263   } else {
  264     $self->{selectedUserIDs} = [];
  265   }
  266 
  267   $self->{editMode} = $r->param("editMode") || 0;
  268 
  269   $self->{sortField} = $r->param("sortField") || "last_name";
  270 
  271   my @allUsers = $db->getUsers(@allUserIDs);
  272   my (%sections, %recitations);
  273   foreach my $User (@allUsers) {
  274     push @{$sections{$User->section}}, $User->user_id;
  275     push @{$recitations{$User->recitation}}, $User->user_id;
  276   }
  277   $self->{sections} = \%sections;
  278   $self->{recitations} = \%recitations;
  279 
  280   ########## call action handler
  281 
  282   my $actionID = $r->param("action");
  283   if ($actionID) {
  284     unless (grep { $_ eq $actionID } @{ VIEW_FORMS() }, @{ EDIT_FORMS() }) {
  285       die "Action $actionID not found";
  286     }
  287     my $actionHandler = "${actionID}_handler";
  288     my %genericParams;
  289     foreach my $param (qw(selected_users)) {
  290       $genericParams{$param} = [ $r->param($param) ];
  291     }
  292     my %actionParams = $self->getActionParams($actionID);
  293     my %tableParams = $self->getTableParams();
  294     print CGI::p(
  295       "Result of last action performed: ",
  296       CGI::i($self->$actionHandler(\%genericParams, \%actionParams, \%tableParams))
  297     );
  298   }
  299 
  300   ########## retrieve possibly changed values for member fields
  301 
  302   #@allUserIDs = @{ $self->{allUserIDs} }; # do we need this one?
  303   my @visibleUserIDs = @{ $self->{visibleUserIDs} };
  304   my @prevVisibleUserIDs = @{ $self->{prevVisibleUserIDs} };
  305   my @selectedUserIDs = @{ $self->{selectedUserIDs} };
  306   my $editMode = $self->{editMode};
  307   my $sortField = $self->{sortField};
  308 
  309   #warn "visibleUserIDs=@visibleUserIDs\n";
  310   #warn "prevVisibleUserIDs=@prevVisibleUserIDs\n";
  311   #warn "selectedUserIDs=@selectedUserIDs\n";
  312   #warn "editMode=$editMode\n";
  313 
  314   ########## get required users
  315 
  316   my @Users = grep { defined $_ } @visibleUserIDs ? $db->getUsers(@visibleUserIDs) : ();
  317 
  318   # presort users
  319   my %sortSubs = %{ SORT_SUBS() };
  320   my $sortSub = $sortSubs{$sortField};
  321   #@Users = sort $sortSub @Users;
  322   @Users = sort byLnFnUid @Users;
  323 
  324   my @PermissionLevels;
  325 
  326   for (my $i = 0; $i < @Users; $i++) {
  327     my $User = $Users[$i];
  328     my $PermissionLevel = $db->getPermissionLevel($User->user_id);
  329 
  330     unless ($PermissionLevel) {
  331       # uh oh! no permission level record found!
  332       warn "added missing permission level for user ", $User->user_id, "\n";
  333 
  334       # create a new permission level record
  335       $PermissionLevel = $db->newPermissionLevel;
  336       $PermissionLevel->user_id($User->user_id);
  337       $PermissionLevel->permission(0);
  338 
  339       # add it to the database
  340       $db->addPermissionLevel($PermissionLevel);
  341     }
  342 
  343     $PermissionLevels[$i] = $PermissionLevel;
  344   }
  345 
  346   ########## print beginning of form
  347 
  348   print CGI::start_form({method=>"post", action=>$r->uri, name=>"userlist"});
  349   print $self->hidden_authen_fields();
  350 
  351   ########## print state data
  352 
  353   print "\n<!-- state data here -->\n";
  354 
  355   if (@visibleUserIDs) {
  356     print CGI::hidden(-name=>"visible_users", -value=>\@visibleUserIDs);
  357   } else {
  358     print CGI::hidden(-name=>"no_visible_users", -value=>"1");
  359   }
  360 
  361   if (@prevVisibleUserIDs) {
  362     print CGI::hidden(-name=>"prev_visible_users", -value=>\@prevVisibleUserIDs);
  363   } else {
  364     print CGI::hidden(-name=>"no_prev_visible_users", -value=>"1");
  365   }
  366 
  367   print CGI::hidden(-name=>"editMode", -value=>$editMode);
  368 
  369   print CGI::hidden(-name=>"sortField", -value=>$sortField);
  370 
  371   print "\n<!-- state data here -->\n";
  372 
  373   ########## print action forms
  374 
  375   print CGI::start_table({});
  376   print CGI::Tr({}, CGI::td({-colspan=>2}, "Select an action to perform:"));
  377 
  378   my @formsToShow;
  379   if ($editMode) {
  380     @formsToShow = @{ EDIT_FORMS() };
  381   } else {
  382     @formsToShow = @{ VIEW_FORMS() };
  383   }
  384 
  385   my $i = 0;
  386   foreach my $actionID (@formsToShow) {
  387     my $actionForm = "${actionID}_form";
  388     my $onChange = "document.userlist.action[$i].checked=true";
  389     my %actionParams = $self->getActionParams($actionID);
  390 
  391     print CGI::Tr({-valign=>"top"},
  392       CGI::td({}, CGI::input({-type=>"radio", -name=>"action", -value=>$actionID})),
  393       CGI::td({}, $self->$actionForm($onChange, %actionParams))
  394     );
  395 
  396     $i++;
  397   }
  398 
  399   print CGI::Tr({}, CGI::td({-colspan=>2, -align=>"center"},
  400     CGI::submit(-value=>"Take Action!"))
  401   );
  402   print CGI::end_table();
  403 
  404   ########## print table
  405 
  406   print CGI::p("Showing ", scalar @visibleUserIDs, " out of ", scalar @allUserIDs, " users.");
  407 
  408   $self->printTableHTML(\@Users, \@PermissionLevels, \%prettyFieldNames,
  409     editMode => $editMode,
  410     selectedUserIDs => \@selectedUserIDs,
  411   );
  412 
  413 
  414   ########## print end of form
  415 
  416   print CGI::end_form();
  417 
  418   return "";
  419 }
  420 
  421 ################################################################################
  422 # extract particular params and put them in a hash (values are ARRAYREFs!)
  423 ################################################################################
  424 
  425 sub getActionParams {
  426   my ($self, $actionID) = @_;
  427   my $r = $self->{r};
  428 
  429   my %actionParams;
  430   foreach my $param ($r->param) {
  431     next unless $param =~ m/^action\.$actionID\./;
  432     $actionParams{$param} = [ $r->param($param) ];
  433   }
  434   return %actionParams;
  435 }
  436 
  437 sub getTableParams {
  438   my ($self) = @_;
  439   my $r = $self->{r};
  440 
  441   my %tableParams;
  442   foreach my $param ($r->param) {
  443     next unless $param =~ m/^(?:user|permission)\./;
  444     $tableParams{$param} = [ $r->param($param) ];
  445   }
  446   return %tableParams;
  447 }
  448 
  449 ################################################################################
  450 # actions and action triggers
  451 ################################################################################
  452 
  453 # filter, edit, cancelEdit, and saveEdit should stay with the display module and
  454 # not be real "actions". that way, all actions are shown in view mode and no
  455 # actions are shown in edit mode.
  456 
  457 sub filter_form {
  458   my ($self, $onChange, %actionParams) = @_;
  459   #return CGI::table({}, CGI::Tr({-valign=>"top"},
  460   # CGI::td({},
  461   return join("",
  462       "Show ",
  463       CGI::popup_menu(
  464         -name => "action.filter.scope",
  465         -values => [qw(all none selected match_ids match_section match_recitation)],
  466         -default => $actionParams{"action.filter.scope"}->[0] || "selected",
  467         -labels => {
  468           all => "all users",
  469           none => "no users",
  470           selected => "users selected below",
  471           match_ids => "users with matching user IDs:",
  472           match_section => "users in selected section",
  473           match_recitation => "users in selected recitation",
  474         },
  475         -onchange => $onChange,
  476       ),
  477       " ",
  478       CGI::textfield(
  479         -name => "action.filter.user_ids",
  480         -value => $actionParams{"action.filter.user_ids"}->[0] || "",,
  481         -width => "50",
  482         -onchange => $onChange,
  483       ),
  484       " (separate multiple IDs with commas)",
  485       CGI::br(),
  486       "sections: ",
  487       CGI::popup_menu(
  488         -name => "action.filter.section",
  489         -values => [ keys %{ $self->{sections} } ],
  490         -default => $actionParams{"action.filter.section"}->[0] || "",
  491         -labels => { $self->menuLabels($self->{sections}) },
  492         -onchange => $onChange,
  493       ),
  494       " recitations: ",
  495       CGI::popup_menu(
  496         -name => "action.filter.recitation",
  497         -values => [ keys %{ $self->{recitations} } ],
  498         -default => $actionParams{"action.filter.recitation"}->[0] || "",
  499         -labels => { $self->menuLabels($self->{recitations}) },
  500         -onchange => $onChange,
  501       ),
  502   );
  503   # ),
  504   #));
  505 }
  506 
  507 # this action handler modifies the "visibleUserIDs" field based on the contents
  508 # of the "action.filter.scope" parameter and the "selected_users"
  509 sub filter_handler {
  510   my ($self, $genericParams, $actionParams, $tableParams) = @_;
  511 
  512   my $result;
  513 
  514   my $scope = $actionParams->{"action.filter.scope"}->[0];
  515   if ($scope eq "all") {
  516     $result = "showing all users";
  517     $self->{visibleUserIDs} = $self->{allUserIDs};
  518   } elsif ($scope eq "none") {
  519     $result = "showing no users";
  520     $self->{visibleUserIDs} = [];
  521   } elsif ($scope eq "selected") {
  522     $result = "showing selected users";
  523     $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
  524   } elsif ($scope eq "match_ids") {
  525     my @userIDs = split /\s*,\s*/, $actionParams->{"action.filter.user_ids"}->[0];
  526     $self->{visibleUserIDs} = \@userIDs;
  527   } elsif ($scope eq "match_section") {
  528     my $section = $actionParams->{"action.filter.section"}->[0];
  529     $self->{visibleUserIDs} = $self->{sections}->{$section}; # an arrayref
  530   } elsif ($scope eq "match_recitation") {
  531     my $recitation = $actionParams->{"action.filter.recitation"}->[0];
  532     $self->{visibleUserIDs} = $self->{recitations}->{$recitation}; # an arrayref
  533   }
  534 
  535   return $result;
  536 }
  537 
  538 sub edit_form {
  539   my ($self, $onChange, %actionParams) = @_;
  540   return join("",
  541     "Edit ",
  542     CGI::popup_menu(
  543       -name => "action.edit.scope",
  544       -values => [qw(all visible selected)],
  545       -default => $actionParams{"action.edit.scope"}->[0] || "visible",
  546       -labels => {
  547         all => "all users",
  548         visible => "visible users",
  549         selected => "selected users"
  550       },
  551       -onchange => $onChange,
  552     ),
  553   );
  554 }
  555 
  556 sub edit_handler {
  557   my ($self, $genericParams, $actionParams, $tableParams) = @_;
  558 
  559   my $result;
  560 
  561   my $scope = $actionParams->{"action.edit.scope"}->[0];
  562   if ($scope eq "all") {
  563     $result = "editing all users";
  564     $self->{visibleUserIDs} = $self->{allUserIDs};
  565   } elsif ($scope eq "visible") {
  566     $result = "editing visible users";
  567     # leave visibleUserIDs alone
  568   } elsif ($scope eq "selected") {
  569     $result = "editing selected users";
  570     $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
  571   }
  572   $self->{editMode} = 1;
  573 
  574   return $result;
  575 }
  576 
  577 sub delete_form {
  578   my ($self, $onChange, %actionParams) = @_;
  579   return join("",
  580     "Delete ",
  581     CGI::popup_menu(
  582       -name => "action.delete.scope",
  583       -values => [qw(visible selected)],
  584       -default => $actionParams{"action.delete.scope"}->[0] || "selected",
  585       -labels => {
  586         visible => "visible users",
  587         selected => "selected users"
  588       },
  589       -onchange => $onChange,
  590     ),
  591     CGI::em(" Deletion destroys all user-related data and is not undoable!"),
  592   );
  593 }
  594 
  595 sub delete_handler {
  596   my ($self, $genericParams, $actionParams, $tableParams) = @_;
  597   my $db = $self->{db};
  598   my $scope = $actionParams->{"action.delete.scope"}->[0];
  599 
  600   my @userIDsToDelete;
  601   if ($scope eq "visible") {
  602     @userIDsToDelete = @{ $self->{visibleUserIDs} };
  603   } elsif ($scope eq "selected") {
  604     @userIDsToDelete = @{ $self->{selectedUserIDs} };
  605   }
  606 
  607   my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
  608   my %visibleUserIDs = map { $_ => 1 } @{ $self->{visibleUserIDs} };
  609   my %selectedUserIDs = map { $_ => 1 } @{ $self->{selectedUserIDs} };
  610 
  611   foreach my $userID (@userIDsToDelete) {
  612     delete $allUserIDs{$userID};
  613     delete $visibleUserIDs{$userID};
  614     delete $selectedUserIDs{$userID};
  615     $db->deleteUser($userID);
  616   }
  617 
  618   $self->{allUserIDs} = [ keys %allUserIDs ];
  619   $self->{visibleUserIDs} = [ keys %visibleUserIDs ];
  620   $self->{selectedUserIDs} = [ keys %selectedUserIDs ];
  621 
  622   my $num = @userIDsToDelete;
  623   return "deleted $num user" . ($num == 1 ? "" : "s");
  624 }
  625 
  626 sub import_form {
  627   my ($self, $onChange, %actionParams) = @_;
  628   return join(" ",
  629     "Import users from file",
  630     CGI::popup_menu(
  631       -name => "action.import.source",
  632       -values => [ "", $self->getCSVList() ],
  633       -default => $actionParams{"action.import.source"}->[0] || "",
  634       -onchange => $onChange,
  635     ),
  636     "replacing",
  637     CGI::popup_menu(
  638       -name => "action.import.replace",
  639       -values => [qw(any visible selected none)],
  640       -default => $actionParams{"action.import.replace"}->[0] || "none",
  641       -labels => {
  642         any => "any",
  643         visible => "visible",
  644         selected => "selected",
  645         none => "no",
  646       },
  647       -onchange => $onChange,
  648     ),
  649     "existing users and adding",
  650     CGI::popup_menu(
  651       -name => "action.import.add",
  652       -values => [qw(any none)],
  653       -default => $actionParams{"action.import.add"}->[0] || "any",
  654       -labels => {
  655         any => "any",
  656         none => "no",
  657       },
  658       -onchange => $onChange,
  659     ),
  660     "new users",
  661   );
  662 }
  663 
  664 sub import_handler {
  665   my ($self, $genericParams, $actionParams, $tableParams) = @_;
  666 
  667   my $source = $actionParams->{"action.import.source"}->[0];
  668   my $add = $actionParams->{"action.import.add"}->[0];
  669   my $replace = $actionParams->{"action.import.replace"}->[0];
  670 
  671   my $fileName = $source;
  672   my $createNew = $add eq "any";
  673   my $replaceExisting;
  674   my @replaceList;
  675   if ($replace eq "any") {
  676     $replaceExisting = "any";
  677   } elsif ($replace eq "none") {
  678     $replaceExisting = "none";
  679   } elsif ($replace eq "visible") {
  680     $replaceExisting = "listed";
  681     @replaceList = @{ $self->{visibleUserIDs} };
  682   } elsif ($replace eq "selected") {
  683     $replaceExisting = "listed";
  684     @replaceList = @{ $self->{selectedUserIDs} };
  685   }
  686 
  687   my ($replaced, $added, $skipped)
  688     = $self->importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList);
  689 
  690   # make new users visible... do we really want to do this? probably.
  691   push @{ $self->{visibleUserIDs} }, @$added;
  692 
  693   my $numReplaced = @$replaced;
  694   my $numAdded = @$added;
  695   my $numSkipped = @$skipped;
  696 
  697   return $numReplaced . " user" . ($numReplaced == 1 ? "" : "s") . " replaced, "
  698     . $numAdded . " user" . ($numAdded == 1 ? "" : "s") . " added, "
  699     . $numSkipped . " user" . ($numSkipped == 1 ? "" : "s") . " skipped.";
  700 }
  701 
  702 sub export_form {
  703   my ($self, $onChange, %actionParams) = @_;
  704   return join("",
  705     "Export ",
  706     CGI::popup_menu(
  707       -name => "action.export.scope",
  708       -values => [qw(all visible selected)],
  709       -default => $actionParams{"action.export.scope"}->[0] || "visible",
  710       -labels => {
  711         all => "all users",
  712         visible => "visible users",
  713         selected => "selected users"
  714       },
  715       -onchange => $onChange,
  716     ),
  717     " to ",
  718     CGI::popup_menu(
  719       -name=>"action.export.target",
  720       -values => [ "new", $self->getCSVList() ],
  721       -labels => { new => "a new file named:" },
  722       -default => $actionParams{"action.export.target"}->[0] || "",
  723       -onchange => $onChange,
  724     ),
  725     #CGI::br(),
  726     #"new file to create: ",
  727     CGI::textfield(
  728       -name => "action.export.new",
  729       -value => $actionParams{"action.export.new"}->[0] || "",,
  730       -width => "50",
  731       -onchange => $onChange,
  732     ),
  733     CGI::tt(".lst"),
  734   );
  735 }
  736 
  737 sub export_handler {
  738   my ($self, $genericParams, $actionParams, $tableParams) = @_;
  739 
  740   my $scope = $actionParams->{"action.export.scope"}->[0];
  741   my $target = $actionParams->{"action.export.target"}->[0];
  742   my $new = $actionParams->{"action.export.new"}->[0];
  743 
  744   my $fileName;
  745   if ($target eq "new") {
  746     $fileName = $new;
  747   } else {
  748     $fileName = $target;
  749   }
  750 
  751   $fileName .= ".lst" unless $fileName =~ m/\.lst$/;
  752 
  753   my @userIDsToExport;
  754   if ($scope eq "all") {
  755     @userIDsToExport = @{ $self->{allUserIDs} };
  756   } elsif ($scope eq "visible") {
  757     @userIDsToExport = @{ $self->{visibleUserIDs} };
  758   } elsif ($scope eq "selected") {
  759     @userIDsToExport = @{ $self->{selectedUserIDs} };
  760   }
  761 
  762   $self->exportUsersToCSV($fileName, @userIDsToExport);
  763 
  764   return scalar @userIDsToExport . " users exported";
  765 }
  766 
  767 sub cancelEdit_form {
  768   my ($self, $onChange, %actionParams) = @_;
  769   return "Abandon changes";
  770 }
  771 
  772 sub cancelEdit_handler {
  773   my ($self, $genericParams, $actionParams, $tableParams) = @_;
  774   my $r = $self->{r};
  775 
  776   #$self->{selectedUserIDs} = $self->{visibleUserIDs};
  777     # only do the above if we arrived here via "edit selected users"
  778   if (defined $r->param("prev_visible_users")) {
  779     $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
  780   } elsif (defined $r->param("no_prev_visible_users")) {
  781     $self->{visibleUserIDs} = [];
  782   } else {
  783     # leave it alone
  784   }
  785   $self->{editMode} = 0;
  786 
  787   return "changes abandoned";
  788 }
  789 
  790 sub saveEdit_form {
  791   my ($self, $onChange, %actionParams) = @_;
  792   return "Save changes";
  793 }
  794 
  795 sub saveEdit_handler {
  796   my ($self, $genericParams, $actionParams, $tableParams) = @_;
  797   my $r = $self->{r};
  798   my $db = $self->{db};
  799 
  800   my @visibleUserIDs = @{ $self->{visibleUserIDs} };
  801   foreach my $userID (@visibleUserIDs) {
  802     my $User = $db->getUser($userID);
  803     my $PermissionLevel = $db->getPermissionLevel($userID);
  804 
  805     foreach my $field ($User->NONKEYFIELDS()) {
  806       my $param = "user.${userID}.${field}";
  807       if (defined $tableParams->{$param}->[0]) {
  808         $User->$field($tableParams->{$param}->[0]);
  809       }
  810     }
  811 
  812     foreach my $field ($PermissionLevel->NONKEYFIELDS()) {
  813       my $param = "permission.${userID}.${field}";
  814       if (defined $tableParams->{$param}->[0]) {
  815         $PermissionLevel->$field($tableParams->{$param}->[0]);
  816       }
  817     }
  818 
  819     $db->putUser($User);
  820     $db->putPermissionLevel($PermissionLevel);
  821   }
  822 
  823   if (defined $r->param("prev_visible_users")) {
  824     $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
  825   } elsif (defined $r->param("no_prev_visible_users")) {
  826     $self->{visibleUserIDs} = [];
  827   } else {
  828     # leave it alone
  829   }
  830 
  831   $self->{editMode} = 0;
  832 
  833   return "changes saved";
  834 }
  835 
  836 ################################################################################
  837 # sorts
  838 ################################################################################
  839 
  840 sub byUserID       { $a->user_id       cmp $b->user_id       }
  841 sub byFirstName    { $a->first_name    cmp $b->first_name    }
  842 sub byLastName     { $a->last_name     cmp $b->last_name     }
  843 sub byEmailAddress { $a->email_address cmp $b->email_address }
  844 sub byStudentID    { $a->student_id    cmp $b->student_id    }
  845 sub byStatus       { $a->status        cmp $b->status        }
  846 sub bySection      { $a->section       cmp $b->section       }
  847 sub byRecitation   { $a->recitation    cmp $b->recitation    }
  848 sub byComment      { $a->comment       cmp $b->comment       }
  849 
  850 sub byLnFnUid { &byLastName || &byFirstName || &byUserID }
  851 
  852 ################################################################################
  853 # utilities
  854 ################################################################################
  855 
  856 # generate labels for section/recitation popup menus
  857 sub menuLabels {
  858   my ($self, $hashRef) = @_;
  859   my %hash = %$hashRef;
  860 
  861   my %result;
  862   foreach my $key (keys %hash) {
  863     my $count = @{ $hash{$key} };
  864     my $displayKey = $key || "<none>";
  865     $result{$key} = "$displayKey ($count users)";
  866   }
  867   return %result;
  868 }
  869 
  870 sub importUsersFromCSV {
  871   my ($self, $fileName, $createNew, $replaceExisting, @replaceList) = @_;
  872   my $ce = $self->{ce};
  873   my $db = $self->{db};
  874   my $dir = $ce->{courseDirs}->{templates};
  875 
  876   die "illegal character in input: \"/\"" if $fileName =~ m|/|;
  877   die "won't be able to read from file $dir/$fileName: does it exist? is it readable?"
  878     unless -r "$dir/$fileName";
  879 
  880   my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
  881   my %replaceOK;
  882   if ($replaceExisting eq "none") {
  883     %replaceOK = ();
  884   } elsif ($replaceExisting eq "listed") {
  885     %replaceOK = map { $_ => 1 } @replaceList;
  886   } elsif ($replaceExisting eq "any") {
  887     %replaceOK = %allUserIDs;
  888   }
  889 
  890   my (@replaced, @added, @skipped);
  891 
  892   my @contents = split /\n/, readFile("$dir/$fileName");
  893   foreach my $string (@contents) {
  894     $string =~ s/^\s+//;
  895     $string =~ s/\s+$//;
  896     my (
  897       $student_id, $last_name, $first_name, $status, $comment,
  898       $section, $recitation, $email_address, $user_id
  899     ) = split /\s*,\s*/, $string;
  900 
  901     if (exists $allUserIDs{$user_id} and not exists $replaceOK{$user_id}) {
  902       push @skipped, $user_id;
  903       next;
  904     }
  905 
  906     if (not exists $allUserIDs{$user_id} and not $createNew) {
  907       push @skipped, $user_id;
  908       next;
  909     }
  910 
  911     my $User = $db->newUser;
  912     $User->user_id($user_id);
  913     $User->first_name($first_name);
  914     $User->last_name($last_name);
  915     $User->email_address($email_address);
  916     $User->student_id($student_id);
  917     $User->status($status);
  918     $User->section($section);
  919     $User->recitation($recitation);
  920     $User->comment($comment);
  921 
  922     my $PermissionLevel = $db->newPermissionLevel;
  923     $PermissionLevel->user_id($user_id);
  924     $PermissionLevel->permission(0);
  925 
  926     my $Password = $db->newPassword;
  927     $Password->user_id($user_id);
  928     $Password->password(cryptPassword($student_id));
  929 
  930     if (exists $allUserIDs{$user_id}) {
  931       $db->putUser($User);
  932       $db->putPermissionLevel($PermissionLevel);
  933       $db->putPassword($Password);
  934       push @replaced, $user_id;
  935     } else {
  936       $db->addUser($User);
  937       $db->addPermissionLevel($PermissionLevel);
  938       $db->addPassword($Password);
  939       push @added, $user_id;
  940     }
  941   }
  942 
  943   return \@replaced, \@added, \@skipped;
  944 }
  945 
  946 sub exportUsersToCSV {
  947   my ($self, $fileName, @userIDsToExport) = @_;
  948   my $ce = $self->{ce};
  949   my $db = $self->{db};
  950   my $dir = $ce->{courseDirs}->{templates};
  951 
  952   die "illegal character in input: \"/\"" if $fileName =~ m|/|;
  953 
  954   open my $fh, ">", "$dir/$fileName"
  955     or die "failed to open file $dir/$fileName for writing: $!\n";
  956 
  957   foreach my $userID (@userIDsToExport) {
  958     my $User = $db->getUser($userID);
  959     my @fields = (
  960       $User->student_id,
  961       $User->last_name,
  962       $User->first_name,
  963       $User->status,
  964       $User->comment,
  965       $User->section,
  966       $User->recitation,
  967       $User->email_address,
  968       $User->user_id,
  969     );
  970     my $string = join ",", @fields;
  971     print $fh "$string\n";
  972   }
  973 
  974   close $fh;
  975 }
  976 
  977 ################################################################################
  978 # "display" methods
  979 ################################################################################
  980 
  981 sub fieldEditHTML {
  982   my ($self, $fieldName, $value, $properties) = @_;
  983   my $size = $properties->{size};
  984   my $type = $properties->{type};
  985   my $access = $properties->{access};
  986   my $items = $properties->{items};
  987   my $synonyms = $properties->{synonyms};
  988 
  989   if ($access eq "readonly") {
  990     return $value;
  991   }
  992 
  993   if ($type eq "number" or $type eq "text") {
  994     return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size});
  995   }
  996 
  997   if ($type eq "enumerable") {
  998     my $matched = undef; # Whether a synonym match has occurred
  999 
 1000     # Process synonyms for enumerable objects
 1001     foreach my $synonym (keys %$synonyms) {
 1002       if ($synonym ne "*" and $value =~ m/$synonym/) {
 1003         $value = $synonyms->{$synonym};
 1004         $matched = 1;
 1005       }
 1006     }
 1007 
 1008     if (!$matched and exists $synonyms->{"*"}) {
 1009       $value = $synonyms->{"*"};
 1010     }
 1011 
 1012     return CGI::popup_menu({
 1013       name => $fieldName,
 1014       values => [keys %$items],
 1015       default => $value,
 1016       labels => $items,
 1017     });
 1018   }
 1019 }
 1020 
 1021 sub recordEditHTML {
 1022   my ($self, $User, $PermissionLevel, %options) = @_;
 1023   my $r = $self->{r};
 1024   my $ce = $self->{ce};
 1025   my $root = $ce->{webworkURLs}->{root};
 1026   my $courseName = $ce->{courseName};
 1027 
 1028   my $editMode = $options{editMode};
 1029   my $userSelected = $options{userSelected};
 1030 
 1031   my $changeEUserURL = "$root/$courseName?"
 1032     . "user="           . $r->param("user")
 1033     . "&effectiveUser=" . $User->user_id
 1034     . "&key="           . $r->param("key");
 1035 
 1036   my $setsAssignedToUserURL = "$root/$courseName/instructor/users/"
 1037     . $User->user_id . "/sets/?"
 1038     . "user="           . $r->param("user")
 1039     . "&effectiveUser=" . $r->param("effectiveUser")
 1040     . "&key="           . $r->param("key");
 1041 
 1042   my @tableCells;
 1043 
 1044   # Select
 1045   if ($editMode) {
 1046     # column not there
 1047   } else {
 1048     # selection checkbox
 1049     push @tableCells, CGI::checkbox(
 1050       -name => "selected_users",
 1051       -value => $User->user_id,
 1052       -checked => $userSelected,
 1053       -label => "",
 1054     );
 1055   }
 1056 
 1057   # Act As
 1058   if ($editMode) {
 1059     # column not there
 1060   } else {
 1061     # selection checkbox
 1062     push @tableCells, CGI::a({href=>$changeEUserURL}, "Act&nbsp;As");
 1063   }
 1064 
 1065   # User ID
 1066   if ($editMode) {
 1067     # straight user ID
 1068     push @tableCells, $User->user_id;
 1069   } else {
 1070     # "edit sets assigned to user" link
 1071     push @tableCells, CGI::a({href=>$setsAssignedToUserURL}, $User->user_id);
 1072   }
 1073 
 1074   # User Fields
 1075   foreach my $field ($User->NONKEYFIELDS) {
 1076     my $fieldName = "user." . $User->user_id . "." . $field,
 1077     my $fieldValue = $User->$field;
 1078     my %properties = %{ FIELD_PROPERTIES()->{$field} };
 1079     $properties{access} = "readonly" unless $editMode;
 1080     push @tableCells, $self->fieldEditHTML($fieldName, $fieldValue, \%properties);
 1081   }
 1082 
 1083   # PermissionLevel Fields
 1084   foreach my $field ($PermissionLevel->NONKEYFIELDS) {
 1085     my $fieldName = "permission." . $PermissionLevel->user_id . "." . $field,
 1086     my $fieldValue = $PermissionLevel->$field;
 1087     my %properties = %{ FIELD_PROPERTIES()->{$field} };
 1088     $properties{access} = "readonly" unless $editMode;
 1089     push @tableCells, $self->fieldEditHTML($fieldName, $fieldValue, \%properties);
 1090   }
 1091 
 1092   return CGI::Tr({}, CGI::td({}, \@tableCells));
 1093 }
 1094 
 1095 sub printTableHTML {
 1096   my ($self, $UsersRef, $PermissionLevelsRef, $fieldNamesRef, %options) = @_;
 1097   my $r = $self->{r};
 1098   my $userTemplate = $self->{userTemplate};
 1099   my $permissionLevelTemplate = $self->{permissionLevelTemplate};
 1100   my @Users = @$UsersRef;
 1101   my @PermissionLevels = @$PermissionLevelsRef;
 1102   my %fieldNames = %$fieldNamesRef;
 1103 
 1104   my $editMode = $options{editMode};
 1105   my %selectedUserIDs = map { $_ => 1 } @{ $options{selectedUserIDs} };
 1106   my $currentSort = $options{currentSort};
 1107 
 1108   # names of headings:
 1109   my @realFieldNames = (
 1110       $userTemplate->KEYFIELDS,
 1111       $userTemplate->NONKEYFIELDS,
 1112       $permissionLevelTemplate->NONKEYFIELDS,
 1113   );
 1114 
 1115   my %sortSubs = %{ SORT_SUBS() };
 1116   #my @stateParams = @{ STATE_PARAMS() };
 1117   #my $hrefPrefix = $r->uri . "?" . $self->url_args(@stateParams); # $self->url_authen_args
 1118   my @tableHeadings;
 1119   foreach my $field (@realFieldNames) {
 1120     my $result;
 1121     #if (exists $sortSubs{$field}) {
 1122     # $result = CGI::a({-href=>"$hrefPrefix&sort=$field"}, $fieldNames{$field});
 1123     #} else {
 1124       $result = $fieldNames{$field};
 1125     #}
 1126     push @tableHeadings, $result;
 1127   };
 1128 
 1129   # prepend selection checkbox? only if we're NOT editing!
 1130   unshift @tableHeadings, "Select", "Act As" unless $editMode;
 1131 
 1132   # print the table
 1133   if ($editMode) {
 1134     print CGI::start_table({});
 1135   } else {
 1136     print CGI::start_table({-border=>1});
 1137   }
 1138 
 1139   print CGI::Tr({}, CGI::th({}, \@tableHeadings));
 1140 
 1141   for (my $i = 0; $i < @Users; $i++) {
 1142     my $User = $Users[$i];
 1143     my $PermissionLevel = $PermissionLevels[$i];
 1144 
 1145     print $self->recordEditHTML($User, $PermissionLevel,
 1146       editMode => $editMode,
 1147       userSelected => exists $selectedUserIDs{$User->user_id}
 1148     );
 1149   }
 1150 
 1151   print CGI::end_table();
 1152 }
 1153 
 1154 1;
 1155 
 1156 __END__
 1157 
 1158   my $editMode = 0;
 1159   if (defined $r->param("edit_selected") or defined $r->param("edit_visible")) {
 1160     $editMode = 1;
 1161   }
 1162 
 1163   my @userIDs = $db->listUsers;
 1164   my @userRecords = $db->getUsers(@userIDs);
 1165 
 1166   my (%sections, %recitations);
 1167   foreach my $user (@userRecords) {
 1168     push @{$sections{$user->section}}, $user;
 1169     push @{$recitations{$user->recitation}}, $user;
 1170   }
 1171 
 1172   my $filter_type = $r->param("filter_type")
 1173     || (@userIDs > HIDE_USERS_THRESHHOLD ? "none" : "all");
 1174   my $filter_user_id = $filter_type eq "filter_user_id"
 1175     ? $r->param("filter_user_id") || ""
 1176     : "";
 1177   my $filter_section = $filter_type eq "filter_section"
 1178     ? $r->param("filter_section") || ""
 1179     : "";
 1180   my $filter_recitation = $filter_type eq "filter_recitation"
 1181     ? $r->param("filter_recitation") || ""
 1182     : "";
 1183 
 1184   # override filter selection if "Edit Selected Users" button is pressed
 1185   if (defined $r->param("edit_selected")) {
 1186     $filter_type = "filter_selected";
 1187   }
 1188 
 1189   if ($filter_type eq "none") {
 1190     @userRecords = ();
 1191   } elsif ($filter_type eq "filter_selected") {
 1192     @userRecords = ();
 1193     my @userIDs = $r->param("selectUser");
 1194     if (@userIDs) {
 1195       @userRecords = $db->getUsers(@userIDs);
 1196     }
 1197   } elsif ($filter_type eq "filter_user_id") {
 1198     @userRecords = ();
 1199     if ($filter_user_id ne "") {
 1200       my $userRecord = $db->getUser($filter_user_id);
 1201       @userRecords = ($userRecord) if $userRecord;
 1202     }
 1203   } elsif ($filter_type eq "filter_section") {
 1204     @userRecords = ();
 1205     @userRecords = @{$sections{$filter_section}}
 1206       if exists $sections{$filter_section};
 1207   } elsif ($filter_type eq "filter_recitation") {
 1208     @userRecords = ();
 1209     @userRecords = @{$recitations{$filter_recitation}}
 1210       if exists $recitations{$filter_recitation};
 1211   }
 1212 
 1213   @userRecords = sort {
 1214     (lc $a->section cmp lc $b->section)
 1215       || (lc $a->last_name cmp lc $b->last_name)
 1216       || (lc $a->first_name cmp lc $b->first_name)
 1217       || (lc $a->user_id cmp lc $b->user_id)
 1218   } @userRecords;
 1219 
 1220   print CGI::start_form({method=>"post", action=>$r->uri()});
 1221   print $self->hidden_authen_fields();
 1222 
 1223   filter options
 1224   my %labels = (
 1225     none => "No users",
 1226     all => "All " . scalar @userIDs . " users",
 1227     filter_selected => "Users selected below",
 1228     filter_user_id => "User with ID " . CGI::input({
 1229       type=>"text",
 1230       name=>"filter_user_id",
 1231       value=>$filter_user_id,
 1232       size=>"20"
 1233     }),
 1234     filter_section => "Users in section " . CGI::popup_menu(
 1235       -name=>"filter_section",
 1236       -values=>[ keys %sections ],
 1237       -labels=>{ $self->menuLabels(\%sections) },
 1238       -default=>$filter_section,
 1239     ),
 1240     filter_recitation => "Users in recitation " . CGI::popup_menu(
 1241       -name=>"filter_recitation",
 1242       -values=>[ sort keys %recitations ],
 1243       -labels=>{ $self->menuLabels(\%recitations) },
 1244       -default=>$filter_recitation,
 1245     ),
 1246   );
 1247 
 1248   if ($editMode) {
 1249     print CGI::hidden(
 1250       -name=>"filter_type",
 1251       -value=>"filter_selected",
 1252     );
 1253   } else {
 1254     my $cgi = new CGI;
 1255     $cgi->autoEscape(0);
 1256     print "Show:", CGI::br();
 1257     print $cgi->radio_group(
 1258       -name=>"filter_type",
 1259       -values=>[ qw(none all filter_selected filter_user_id filter_section filter_recitation) ],
 1260       -default=>$filter_type,
 1261       -linebreak=>"true",
 1262       -labels=>\%labels,
 1263       -rows=>3,
 1264       -columns=>2,
 1265     );
 1266     print CGI::submit({name=>"filter", value=>"Filter"});
 1267   }
 1268 
 1269   print CGI::start_table({});
 1270 
 1271   # Table headings, prettied-up
 1272   my @tableHeadings = (
 1273     ($editMode ? () : "Select"),
 1274     map {$prettyFieldNames{$_}} (
 1275       $userTemplate->KEYFIELDS(),
 1276       $userTemplate->NONKEYFIELDS(),
 1277       $permissionLevelTemplate->NONKEYFIELDS(),
 1278     ),
 1279   );
 1280 
 1281   # now print them
 1282   print CGI::Tr({},
 1283     CGI::th({}, \@tableHeadings)
 1284   );
 1285 
 1286   my @userIDsForHiddenSelectField;
 1287 
 1288   # process user records
 1289   foreach my $userRecord (@userRecords) {
 1290     my $currentUser = $userRecord->user_id;
 1291     push @userIDsForHiddenSelectField, $currentUser;
 1292     my $permissionLevel = $db->getPermissionLevel($currentUser);
 1293     unless (defined $permissionLevel) {
 1294       warn "No permissionLevel record for user $currentUser -- added";
 1295       my $newPermissionLevel = $db->newPermissionLevel;
 1296       $newPermissionLevel->user_id($currentUser);
 1297       $newPermissionLevel->permission(0);
 1298       $db->addPermissionLevel($newPermissionLevel);
 1299       $permissionLevel = $newPermissionLevel;
 1300       # permission set to minimum level
 1301     }
 1302 
 1303     # A concise way of printing a row containing a cell for each field, editable unless it's a key
 1304     print CGI::Tr({},
 1305       CGI::td({}, [
 1306         ($editMode
 1307           ? () # don't show selection checkbox if we're in edit mode -- hidden field below
 1308           #: CGI::input({type=>"checkbox", name=>"selectUser", value=>$currentUser})
 1309           : CGI::checkbox(
 1310             -name=>"selectUser",
 1311             -value=>$currentUser,
 1312             -checked=>($filter_type eq "filter_selected" and not defined $r->param("editingAllVisibleUsers")),
 1313             -label=>""
 1314           )
 1315         ),
 1316         ($editMode
 1317           ? $currentUser
 1318           : (map {
 1319             my $changeEUserURL = "$root/$courseName?"
 1320               . "user=" . $r->param("user")
 1321               . "&effectiveUser=" . $userRecord->user_id
 1322               . "&key=" . $r->param("key");
 1323             CGI::a({href=>$changeEUserURL}, $userRecord->$_)
 1324           } $userRecord->KEYFIELDS)
 1325         ),
 1326         (map {
 1327           $self->fieldEditHTML(
 1328             "user." . $userRecord->user_id . "." .$_,
 1329             $userRecord->$_, $fieldProperties{$_});
 1330         } $userRecord->NONKEYFIELDS()),
 1331         (map {
 1332           $self->fieldEditHTML(
 1333             "permission." . $permissionLevel->user_id . "." . $_,
 1334             $permissionLevel->$_, $fieldProperties{$_});
 1335         } $permissionLevel->NONKEYFIELDS()),
 1336       ])
 1337     );
 1338   }
 1339 
 1340   unless (@userRecords) {
 1341     print CGI::Tr({},
 1342       CGI::td({-colspan=>scalar(@tableHeadings), -align=>"center"},
 1343         "No users match the filter criteria above."
 1344       ),
 1345     );
 1346   }
 1347 
 1348   print CGI::end_table();
 1349 
 1350   if ($editMode) {
 1351     print CGI::hidden(-name=>"selectUser", -value=>[ @userIDsForHiddenSelectField ]);
 1352     if (defined $r->param("edit_visible")) {
 1353       print CGI::hidden(-name=>"editingAllVisibleUsers", -value=>1);
 1354     }
 1355   }
 1356 
 1357   if ($editMode) {
 1358     print CGI::submit({name=>"discard_chagnes", value=>"Discard Changes to Users"});
 1359     print CGI::submit({name=>"save_classlist", value=>"Save Changes to Users"});
 1360   } else {
 1361     print CGI::submit({name=>"edit_visible", value=>"Edit Visible Users"});
 1362     print CGI::submit({name=>"edit_selected", value=>"Edit Selected Users"});
 1363     print CGI::submit({name=>"delete_selected", value=>"Delete Selected Users"});
 1364   }
 1365 
 1366   print CGI::end_form();
 1367 
 1368   # Add a student form
 1369   unless ($editMode) {
 1370     print CGI::start_form({method=>"post", action=>$r->uri()});
 1371     print $self->hidden_authen_fields();
 1372     print "User ID:";
 1373     print CGI::input({type=>"text", name=>"newUserID", value=>"", size=>"20"});
 1374     print CGI::submit({name=>"addStudent", value=>"Add User"});
 1375     print CGI::end_form();
 1376   }
 1377 
 1378   return "";

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9