[system] / branches / rel-2-1-patches / webwork2 / lib / WeBWorK / ContentGenerator / Instructor / UserList.pm Repository:
ViewVC logotype

View of /branches/rel-2-1-patches/webwork2/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3122 - (download) (as text) (annotate)
Fri Jan 28 00:47:35 2005 UTC (8 years, 3 months ago) by sh002i
File size: 42048 byte(s)
HEAD backport: new regexp matching can now match on permission level too
(toenail)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9