[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 1736 - (download) (as text) (annotate)
Thu Jan 22 00:25:17 2004 UTC (9 years, 4 months ago) by gage
File size: 41030 byte(s)
Made modifications to header that make it clearer how to edit all
sets for one user.

--Mike

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9