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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2280 - (view) (download) (as text)

1 : sh002i 1567 ################################################################################
2 : sh002i 1663 # WeBWorK Online Homework Delivery System
3 :     # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4 : toenail 2280 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm,v 1.51 2004/06/08 17:13:45 toenail Exp $
5 : sh002i 1663 #
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 : sh002i 1567 ################################################################################
16 :    
17 : malsyned 832 package WeBWorK::ContentGenerator::Instructor::UserList;
18 :     use base qw(WeBWorK::ContentGenerator::Instructor);
19 :    
20 :     =head1 NAME
21 :    
22 : sh002i 1567 WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific
23 :     data editing
24 : malsyned 832
25 :     =cut
26 :    
27 : sh002i 1594 =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 : malsyned 832 use strict;
63 :     use warnings;
64 :     use CGI qw();
65 : gage 2137 use WeBWorK::Utils qw(readFile readDirectory cryptPassword);
66 : gage 1712 use Apache::Constants qw(:common REDIRECT DONE); #FIXME -- this should be called higher up in the object tree.
67 : toenail 2280 use constant HIDE_USERS_THRESHHOLD => 50;
68 : sh002i 1594 use constant EDIT_FORMS => [qw(cancelEdit saveEdit)];
69 : gage 1712 use constant VIEW_FORMS => [qw(filter edit import export add delete)];
70 : sh002i 1594 use constant STATE_PARAMS => [qw(user effectiveUser key visible_users no_visible_users prev_visible_users no_prev_visible_users editMode sortField)];
71 : sh002i 1567
72 : sh002i 1594 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 : gage 1712 sub pre_header_initialize {
148 : gage 1928 my $self = shift;
149 :     my $r = $self->r;
150 :     my $urlpath = $r->urlpath;
151 :     my $ce = $r->ce;
152 :     my $courseName = $urlpath->arg("courseID");
153 : gage 1712 # Handle redirects, if any.
154 :     ##############################
155 :     # Redirect to the addUser page
156 :     ##################################
157 :    
158 :     defined($r->param('action')) && $r->param('action') eq 'add' && do {
159 :     # fix url and redirect
160 :     my $root = $ce->{webworkURLs}->{root};
161 : gage 1928
162 : gage 1712 my $numberOfStudents = $r->param('number_of_students');
163 :     warn "number of students not defined " unless defined $numberOfStudents;
164 : sh002i 1594
165 : gage 2009 my $uri=$self->systemLink( $urlpath->newFromModule('WeBWorK::ContentGenerator::Instructor::AddUsers',courseID=>$courseName),
166 :     params=>{
167 :     number_of_students=>$numberOfStudents,
168 :     }
169 : gage 1938 );
170 : gage 1712 #FIXME does the display mode need to be defined?
171 :     #FIXME url_authen_args also includes an effective user, so the new one must come first.
172 :     # even that might not work with every browser since there are two effective User assignments.
173 :     $r->header_out(Location => $uri);
174 :     $self->{noContent} = 1; # forces redirect
175 :     return;
176 :     };
177 :     }
178 :     # FIXME -- this should be moved up to instructor or contentgenerator
179 :     sub header {
180 :     my $self = shift;
181 :     return REDIRECT if $self->{noContent};
182 : gage 1928 my $r = $self->r;
183 : gage 1712 $r->content_type('text/html');
184 :     $r->send_http_header();
185 :     return OK;
186 :     }
187 : toenail 1799
188 :     #FIXME -- this should probably be moved up to instructor or contentgenerator as well
189 : sh002i 1973 #sub nbsp {
190 :     # my $str = shift;
191 :     # ($str =~/\S/) ? $str : ' ' ; # returns non-breaking space for empty strings
192 :     # # tricky cases: $str =0;
193 :     # # $str is a complex number
194 :     #}
195 :     # moved to ContentGenerator.pm
196 : toenail 1799
197 : malsyned 1015 sub initialize {
198 : malsyned 1211 my ($self) = @_;
199 : gage 1928 my $r = $self->r;
200 :     my $db = $r->db;
201 :     my $ce = $r->ce;
202 :     my $authz = $r->authz;
203 : gage 1712 my $user = $r->param('user');
204 : malsyned 1211
205 :     unless ($authz->hasPermissions($user, "modify_student_data")) {
206 : toenail 2085 $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify student data")));
207 : malsyned 1211 return;
208 :     }
209 :    
210 : sh002i 1601 #if (defined($r->param('addStudent'))) {
211 :     # my $newUser = $db->newUser;
212 :     # my $newPermissionLevel = $db->newPermissionLevel;
213 :     # my $newPassword = $db->newPassword;
214 :     # $newUser->user_id($r->param('newUserID'));
215 :     # $newPermissionLevel->user_id($r->param('newUserID'));
216 :     # $newPassword->user_id($r->param('newUserID'));
217 :     # $newUser->status('C');
218 :     # $newPermissionLevel->permission(0);
219 :     # $db->addUser($newUser);
220 :     # $db->addPermissionLevel($newPermissionLevel);
221 :     # $db->addPassword($newPassword);
222 :     #}
223 : malsyned 1015 }
224 :    
225 : gage 1295
226 :    
227 : malsyned 1014 sub body {
228 : gage 1928 my ($self) = @_;
229 :     my $r = $self->r;
230 :     my $urlpath = $r->urlpath;
231 :     my $db = $r->db;
232 :     my $ce = $r->ce;
233 :     my $authz = $r->authz;
234 :     my $courseName = $urlpath->arg("courseID");
235 :     my $setID = $urlpath->arg("setID");
236 :     my $user = $r->param('user');
237 : sh002i 1584
238 : gage 1928 my $root = $ce->{webworkURLs}->{root};
239 :    
240 : sh002i 1594 # templates for getting field names
241 :     my $userTemplate = $self->{userTemplate} = $db->newUser;
242 :     my $permissionLevelTemplate = $self->{permissionLevelTemplate} = $db->newPermissionLevel;
243 :    
244 : sh002i 1567 return CGI::em("You are not authorized to access the Instructor tools.")
245 :     unless $authz->hasPermissions($user, "access_instructor_tools");
246 : sh002i 1584
247 : malsyned 1214 # This table can be consulted when display-ready forms of field names are needed.
248 : sh002i 1594 my %prettyFieldNames = map { $_ => $_ }
249 :     $userTemplate->FIELDS(),
250 :     $permissionLevelTemplate->FIELDS();
251 : sh002i 1567
252 : malsyned 1214 @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 : gage 1736 "Assigned sets",
265 : malsyned 1214 "First Name",
266 :     "Last Name",
267 :     "E-mail",
268 :     "Student ID",
269 :     "Status",
270 :     "Section",
271 :     "Recitation",
272 :     "Comment",
273 :     "Perm. Level"
274 :     );
275 : sh002i 1567
276 : sh002i 1594 ########## 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 : sh002i 1597 if (@allUserIDs > HIDE_USERS_THRESHHOLD) {
287 :     $self->{visibleUserIDs} = [];
288 :     } else {
289 :     $self->{visibleUserIDs} = [ @allUserIDs ];
290 :     }
291 : sh002i 1594 }
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 : sh002i 1601 my @allUsers = $db->getUsers(@allUserIDs);
306 :     my (%sections, %recitations);
307 :     foreach my $User (@allUsers) {
308 : sh002i 1973 push @{$sections{defined $User->section ? $User->section : ""}}, $User->user_id;
309 :     push @{$recitations{defined $User->recitation ? $User->recitation : ""}}, $User->user_id;
310 : sh002i 1601 }
311 :     $self->{sections} = \%sections;
312 :     $self->{recitations} = \%recitations;
313 :    
314 : sh002i 1594 ########## 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 : gage 1713 '<div style="color:green">',
330 : sh002i 1597 "Result of last action performed: ",
331 : gage 1713 CGI::i($self->$actionHandler(\%genericParams, \%actionParams, \%tableParams)),
332 :     '</div>',
333 :     CGI::hr()
334 :    
335 : sh002i 1594 );
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 : sh002i 1601
354 :     my @Users = grep { defined $_ } @visibleUserIDs ? $db->getUsers(@visibleUserIDs) : ();
355 : sh002i 1594
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 : gage 1667 my $PermissionLevel = $db->getPermissionLevel($User->user_id); # checked
367 : sh002i 1594
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 : gage 1938 print CGI::start_form({method=>"post", action=>$self->systemLink($urlpath,authen=>0), name=>"userlist"});
387 : sh002i 1594 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 : sh002i 1597 ########## 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 : sh002i 1594 ########## 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 : sh002i 1597 # 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 : sh002i 1594 sub filter_form {
496 :     my ($self, $onChange, %actionParams) = @_;
497 : sh002i 1601 #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 : gage 1711 -default => $actionParams{"action.filter.scope"}->[0] || "match_ids",
505 : sh002i 1601 -labels => {
506 :     all => "all users",
507 :     none => "no users",
508 : sh002i 1782 selected => "users checked below",
509 : sh002i 1601 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 : sh002i 1594 );
541 : sh002i 1601 # ),
542 :     #));
543 : sh002i 1594 }
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 : sh002i 1601 } 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 : sh002i 1604 $self->{visibleUserIDs} = $self->{sections}->{$section}; # an arrayref
568 : sh002i 1601 } elsif ($scope eq "match_recitation") {
569 :     my $recitation = $actionParams->{"action.filter.recitation"}->[0];
570 : sh002i 1604 $self->{visibleUserIDs} = $self->{recitations}->{$recitation}; # an arrayref
571 : sh002i 1594 }
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 : gage 1713 -default => $actionParams{"action.edit.scope"}->[0] || "selected",
584 : sh002i 1594 -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 : toenail 2279 CGI::div({class=>"ResultsWithError"},
619 : sh002i 1594 "Delete ",
620 :     CGI::popup_menu(
621 :     -name => "action.delete.scope",
622 : toenail 2279 -values => [qw(none selected)],
623 : gage 1712 -default => $actionParams{"action.delete.scope"}->[0] || "none",
624 : sh002i 1594 -labels => {
625 : gage 1712 none => "no users.",
626 : sh002i 1782 #visible => "visible users.",
627 : gage 1712 selected => "selected users."
628 : sh002i 1594 },
629 :     -onchange => $onChange,
630 :     ),
631 : sh002i 1597 CGI::em(" Deletion destroys all user-related data and is not undoable!"),
632 : toenail 2279 ),
633 : sh002i 1594 );
634 :     }
635 :    
636 :     sub delete_handler {
637 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
638 : gage 1928 my $r = $self->r;
639 :     my $db = $r->db;
640 : sh002i 1594 my $scope = $actionParams->{"action.delete.scope"}->[0];
641 :    
642 : gage 1712 my @userIDsToDelete = ();
643 : sh002i 1782 #if ($scope eq "visible") {
644 :     # @userIDsToDelete = @{ $self->{visibleUserIDs} };
645 :     #} elsif ($scope eq "selected") {
646 :     if ($scope eq "selected") {
647 : sh002i 1594 @userIDsToDelete = @{ $self->{selectedUserIDs} };
648 :     }
649 :    
650 :     my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
651 :     my %visibleUserIDs = map { $_ => 1 } @{ $self->{visibleUserIDs} };
652 :     my %selectedUserIDs = map { $_ => 1 } @{ $self->{selectedUserIDs} };
653 :    
654 :     foreach my $userID (@userIDsToDelete) {
655 :     delete $allUserIDs{$userID};
656 :     delete $visibleUserIDs{$userID};
657 :     delete $selectedUserIDs{$userID};
658 :     $db->deleteUser($userID);
659 :     }
660 :    
661 :     $self->{allUserIDs} = [ keys %allUserIDs ];
662 :     $self->{visibleUserIDs} = [ keys %visibleUserIDs ];
663 :     $self->{selectedUserIDs} = [ keys %selectedUserIDs ];
664 :    
665 :     my $num = @userIDsToDelete;
666 :     return "deleted $num user" . ($num == 1 ? "" : "s");
667 :     }
668 : gage 1712 sub add_form {
669 :     my ($self, $onChange, %actionParams) = @_;
670 : sh002i 1594
671 : gage 1712 return "Add ", CGI::input({name=>'number_of_students', value=>1,size => 3}), " student(s). ";
672 :     }
673 :    
674 :     sub add_handler {
675 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
676 :     # This action is redirected to the addUser.pm module using ../instructor/add_user/...
677 :     return "Nothing done by add student handler";
678 :     }
679 : sh002i 1594 sub import_form {
680 :     my ($self, $onChange, %actionParams) = @_;
681 :     return join(" ",
682 :     "Import users from file",
683 :     CGI::popup_menu(
684 :     -name => "action.import.source",
685 :     -values => [ "", $self->getCSVList() ],
686 :     -default => $actionParams{"action.import.source"}->[0] || "",
687 :     -onchange => $onChange,
688 : sh002i 1597 ),
689 : sh002i 1594 "replacing",
690 :     CGI::popup_menu(
691 :     -name => "action.import.replace",
692 :     -values => [qw(any visible selected none)],
693 :     -default => $actionParams{"action.import.replace"}->[0] || "none",
694 :     -labels => {
695 :     any => "any",
696 :     visible => "visible",
697 :     selected => "selected",
698 :     none => "no",
699 :     },
700 :     -onchange => $onChange,
701 :     ),
702 : sh002i 1597 "existing users and adding",
703 : sh002i 1594 CGI::popup_menu(
704 :     -name => "action.import.add",
705 :     -values => [qw(any none)],
706 :     -default => $actionParams{"action.import.add"}->[0] || "any",
707 :     -labels => {
708 :     any => "any",
709 :     none => "no",
710 :     },
711 :     -onchange => $onChange,
712 :     ),
713 :     "new users",
714 :     );
715 :     }
716 :    
717 :     sub import_handler {
718 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
719 :    
720 :     my $source = $actionParams->{"action.import.source"}->[0];
721 :     my $add = $actionParams->{"action.import.add"}->[0];
722 :     my $replace = $actionParams->{"action.import.replace"}->[0];
723 :    
724 :     my $fileName = $source;
725 :     my $createNew = $add eq "any";
726 :     my $replaceExisting;
727 :     my @replaceList;
728 :     if ($replace eq "any") {
729 :     $replaceExisting = "any";
730 :     } elsif ($replace eq "none") {
731 :     $replaceExisting = "none";
732 :     } elsif ($replace eq "visible") {
733 :     $replaceExisting = "listed";
734 :     @replaceList = @{ $self->{visibleUserIDs} };
735 :     } elsif ($replace eq "selected") {
736 :     $replaceExisting = "listed";
737 :     @replaceList = @{ $self->{selectedUserIDs} };
738 :     }
739 :    
740 :     my ($replaced, $added, $skipped)
741 :     = $self->importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList);
742 :    
743 : sh002i 1597 # make new users visible... do we really want to do this? probably.
744 :     push @{ $self->{visibleUserIDs} }, @$added;
745 :    
746 : sh002i 1594 my $numReplaced = @$replaced;
747 :     my $numAdded = @$added;
748 :     my $numSkipped = @$skipped;
749 :    
750 :     return $numReplaced . " user" . ($numReplaced == 1 ? "" : "s") . " replaced, "
751 :     . $numAdded . " user" . ($numAdded == 1 ? "" : "s") . " added, "
752 :     . $numSkipped . " user" . ($numSkipped == 1 ? "" : "s") . " skipped.";
753 :     }
754 :    
755 :     sub export_form {
756 :     my ($self, $onChange, %actionParams) = @_;
757 : sh002i 1597 return join("",
758 :     "Export ",
759 : sh002i 1594 CGI::popup_menu(
760 :     -name => "action.export.scope",
761 :     -values => [qw(all visible selected)],
762 : sh002i 1597 -default => $actionParams{"action.export.scope"}->[0] || "visible",
763 : sh002i 1594 -labels => {
764 :     all => "all users",
765 :     visible => "visible users",
766 :     selected => "selected users"
767 :     },
768 :     -onchange => $onChange,
769 :     ),
770 : sh002i 1597 " to ",
771 : sh002i 1594 CGI::popup_menu(
772 : sh002i 1597 -name=>"action.export.target",
773 :     -values => [ "new", $self->getCSVList() ],
774 :     -labels => { new => "a new file named:" },
775 :     -default => $actionParams{"action.export.target"}->[0] || "",
776 : sh002i 1594 -onchange => $onChange,
777 : sh002i 1597 ),
778 :     #CGI::br(),
779 :     #"new file to create: ",
780 : sh002i 1594 CGI::textfield(
781 : sh002i 1597 -name => "action.export.new",
782 :     -value => $actionParams{"action.export.new"}->[0] || "",,
783 :     -width => "50",
784 :     -onchange => $onChange,
785 : sh002i 1594 ),
786 : sh002i 1597 CGI::tt(".lst"),
787 : sh002i 1594 );
788 :     }
789 :    
790 : sh002i 1597 sub export_handler {
791 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
792 :    
793 :     my $scope = $actionParams->{"action.export.scope"}->[0];
794 :     my $target = $actionParams->{"action.export.target"}->[0];
795 :     my $new = $actionParams->{"action.export.new"}->[0];
796 :    
797 :     my $fileName;
798 :     if ($target eq "new") {
799 :     $fileName = $new;
800 :     } else {
801 :     $fileName = $target;
802 :     }
803 :    
804 :     $fileName .= ".lst" unless $fileName =~ m/\.lst$/;
805 :    
806 :     my @userIDsToExport;
807 :     if ($scope eq "all") {
808 :     @userIDsToExport = @{ $self->{allUserIDs} };
809 :     } elsif ($scope eq "visible") {
810 :     @userIDsToExport = @{ $self->{visibleUserIDs} };
811 :     } elsif ($scope eq "selected") {
812 :     @userIDsToExport = @{ $self->{selectedUserIDs} };
813 :     }
814 :    
815 :     $self->exportUsersToCSV($fileName, @userIDsToExport);
816 :    
817 :     return scalar @userIDsToExport . " users exported";
818 :     }
819 :    
820 : sh002i 1594 sub cancelEdit_form {
821 :     my ($self, $onChange, %actionParams) = @_;
822 :     return "Abandon changes";
823 :     }
824 :    
825 :     sub cancelEdit_handler {
826 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
827 : gage 1928 my $r = $self->r;
828 : sh002i 1594
829 :     #$self->{selectedUserIDs} = $self->{visibleUserIDs};
830 :     # only do the above if we arrived here via "edit selected users"
831 :     if (defined $r->param("prev_visible_users")) {
832 :     $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
833 :     } elsif (defined $r->param("no_prev_visible_users")) {
834 :     $self->{visibleUserIDs} = [];
835 :     } else {
836 :     # leave it alone
837 :     }
838 :     $self->{editMode} = 0;
839 :    
840 :     return "changes abandoned";
841 :     }
842 :    
843 :     sub saveEdit_form {
844 :     my ($self, $onChange, %actionParams) = @_;
845 :     return "Save changes";
846 :     }
847 :    
848 :     sub saveEdit_handler {
849 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
850 : gage 1928 my $r = $self->r;
851 :     my $db = $r->db;
852 : sh002i 1594
853 :     my @visibleUserIDs = @{ $self->{visibleUserIDs} };
854 :     foreach my $userID (@visibleUserIDs) {
855 : gage 1667 my $User = $db->getUser($userID); # checked
856 :     die "record for visible user $userID not found" unless $User;
857 :     my $PermissionLevel = $db->getPermissionLevel($userID); # checked
858 :     die "permissions for $userID not defined" unless defined $PermissionLevel;
859 : sh002i 1594 foreach my $field ($User->NONKEYFIELDS()) {
860 :     my $param = "user.${userID}.${field}";
861 :     if (defined $tableParams->{$param}->[0]) {
862 :     $User->$field($tableParams->{$param}->[0]);
863 : malsyned 1233 }
864 :     }
865 : sh002i 1594
866 :     foreach my $field ($PermissionLevel->NONKEYFIELDS()) {
867 :     my $param = "permission.${userID}.${field}";
868 :     if (defined $tableParams->{$param}->[0]) {
869 :     $PermissionLevel->$field($tableParams->{$param}->[0]);
870 :     }
871 :     }
872 :    
873 :     $db->putUser($User);
874 :     $db->putPermissionLevel($PermissionLevel);
875 :     }
876 :    
877 :     if (defined $r->param("prev_visible_users")) {
878 :     $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
879 :     } elsif (defined $r->param("no_prev_visible_users")) {
880 :     $self->{visibleUserIDs} = [];
881 :     } else {
882 :     # leave it alone
883 :     }
884 :    
885 :     $self->{editMode} = 0;
886 :    
887 :     return "changes saved";
888 :     }
889 :    
890 :     ################################################################################
891 :     # sorts
892 :     ################################################################################
893 :    
894 :     sub byUserID { $a->user_id cmp $b->user_id }
895 :     sub byFirstName { $a->first_name cmp $b->first_name }
896 :     sub byLastName { $a->last_name cmp $b->last_name }
897 :     sub byEmailAddress { $a->email_address cmp $b->email_address }
898 :     sub byStudentID { $a->student_id cmp $b->student_id }
899 :     sub byStatus { $a->status cmp $b->status }
900 :     sub bySection { $a->section cmp $b->section }
901 :     sub byRecitation { $a->recitation cmp $b->recitation }
902 :     sub byComment { $a->comment cmp $b->comment }
903 :    
904 :     sub byLnFnUid { &byLastName || &byFirstName || &byUserID }
905 :    
906 :     ################################################################################
907 :     # utilities
908 :     ################################################################################
909 :    
910 :     # generate labels for section/recitation popup menus
911 :     sub menuLabels {
912 :     my ($self, $hashRef) = @_;
913 :     my %hash = %$hashRef;
914 :    
915 :     my %result;
916 :     foreach my $key (keys %hash) {
917 :     my $count = @{ $hash{$key} };
918 :     my $displayKey = $key || "<none>";
919 :     $result{$key} = "$displayKey ($count users)";
920 :     }
921 :     return %result;
922 :     }
923 :    
924 :     sub importUsersFromCSV {
925 :     my ($self, $fileName, $createNew, $replaceExisting, @replaceList) = @_;
926 : gage 1928 my $r = $self->r;
927 :     my $ce = $r->ce;
928 :     my $db = $r->db;
929 :     my $dir = $ce->{courseDirs}->{templates};
930 : sh002i 1594
931 :     die "illegal character in input: \"/\"" if $fileName =~ m|/|;
932 :     die "won't be able to read from file $dir/$fileName: does it exist? is it readable?"
933 :     unless -r "$dir/$fileName";
934 :    
935 :     my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
936 :     my %replaceOK;
937 :     if ($replaceExisting eq "none") {
938 :     %replaceOK = ();
939 :     } elsif ($replaceExisting eq "listed") {
940 :     %replaceOK = map { $_ => 1 } @replaceList;
941 :     } elsif ($replaceExisting eq "any") {
942 :     %replaceOK = %allUserIDs;
943 :     }
944 :    
945 :     my (@replaced, @added, @skipped);
946 :    
947 :     my @contents = split /\n/, readFile("$dir/$fileName");
948 :     foreach my $string (@contents) {
949 :     $string =~ s/^\s+//;
950 :     $string =~ s/\s+$//;
951 :     my (
952 :     $student_id, $last_name, $first_name, $status, $comment,
953 :     $section, $recitation, $email_address, $user_id
954 :     ) = split /\s*,\s*/, $string;
955 :    
956 :     if (exists $allUserIDs{$user_id} and not exists $replaceOK{$user_id}) {
957 :     push @skipped, $user_id;
958 :     next;
959 :     }
960 :    
961 :     if (not exists $allUserIDs{$user_id} and not $createNew) {
962 :     push @skipped, $user_id;
963 :     next;
964 :     }
965 :    
966 :     my $User = $db->newUser;
967 :     $User->user_id($user_id);
968 :     $User->first_name($first_name);
969 :     $User->last_name($last_name);
970 :     $User->email_address($email_address);
971 :     $User->student_id($student_id);
972 :     $User->status($status);
973 :     $User->section($section);
974 :     $User->recitation($recitation);
975 :     $User->comment($comment);
976 :    
977 :     my $PermissionLevel = $db->newPermissionLevel;
978 :     $PermissionLevel->user_id($user_id);
979 :     $PermissionLevel->permission(0);
980 :    
981 :     my $Password = $db->newPassword;
982 :     $Password->user_id($user_id);
983 : sh002i 1654 $Password->password(cryptPassword($student_id));
984 : sh002i 1594
985 :     if (exists $allUserIDs{$user_id}) {
986 :     $db->putUser($User);
987 :     $db->putPermissionLevel($PermissionLevel);
988 :     $db->putPassword($Password);
989 :     push @replaced, $user_id;
990 :     } else {
991 :     $db->addUser($User);
992 :     $db->addPermissionLevel($PermissionLevel);
993 :     $db->addPassword($Password);
994 :     push @added, $user_id;
995 :     }
996 :     }
997 :    
998 :     return \@replaced, \@added, \@skipped;
999 :     }
1000 :    
1001 : sh002i 1597 sub exportUsersToCSV {
1002 :     my ($self, $fileName, @userIDsToExport) = @_;
1003 : gage 1928 my $r = $self->r;
1004 :     my $ce = $r->ce;
1005 :     my $db = $r->db;
1006 :     my $dir = $ce->{courseDirs}->{templates};
1007 : sh002i 1597
1008 :     die "illegal character in input: \"/\"" if $fileName =~ m|/|;
1009 :    
1010 :     open my $fh, ">", "$dir/$fileName"
1011 :     or die "failed to open file $dir/$fileName for writing: $!\n";
1012 :    
1013 :     foreach my $userID (@userIDsToExport) {
1014 : gage 1667 my $User = $db->getUser($userID); # checked
1015 :     die "record for user $userID not found." unless $User;
1016 : sh002i 1597 my @fields = (
1017 :     $User->student_id,
1018 :     $User->last_name,
1019 :     $User->first_name,
1020 :     $User->status,
1021 :     $User->comment,
1022 :     $User->section,
1023 :     $User->recitation,
1024 :     $User->email_address,
1025 :     $User->user_id,
1026 :     );
1027 :     my $string = join ",", @fields;
1028 :     print $fh "$string\n";
1029 :     }
1030 :    
1031 :     close $fh;
1032 :     }
1033 :    
1034 : sh002i 1594 ################################################################################
1035 :     # "display" methods
1036 :     ################################################################################
1037 :    
1038 :     sub fieldEditHTML {
1039 :     my ($self, $fieldName, $value, $properties) = @_;
1040 :     my $size = $properties->{size};
1041 :     my $type = $properties->{type};
1042 :     my $access = $properties->{access};
1043 :     my $items = $properties->{items};
1044 :     my $synonyms = $properties->{synonyms};
1045 :    
1046 :     if ($access eq "readonly") {
1047 :     return $value;
1048 :     }
1049 :    
1050 :     if ($type eq "number" or $type eq "text") {
1051 :     return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size});
1052 :     }
1053 :    
1054 :     if ($type eq "enumerable") {
1055 :     my $matched = undef; # Whether a synonym match has occurred
1056 :    
1057 :     # Process synonyms for enumerable objects
1058 :     foreach my $synonym (keys %$synonyms) {
1059 :     if ($synonym ne "*" and $value =~ m/$synonym/) {
1060 :     $value = $synonyms->{$synonym};
1061 :     $matched = 1;
1062 :     }
1063 :     }
1064 :    
1065 :     if (!$matched and exists $synonyms->{"*"}) {
1066 :     $value = $synonyms->{"*"};
1067 :     }
1068 :    
1069 :     return CGI::popup_menu({
1070 :     name => $fieldName,
1071 :     values => [keys %$items],
1072 :     default => $value,
1073 :     labels => $items,
1074 :     });
1075 :     }
1076 :     }
1077 :    
1078 :     sub recordEditHTML {
1079 :     my ($self, $User, $PermissionLevel, %options) = @_;
1080 : gage 1928 my $r = $self->r;
1081 :     my $urlpath = $r->urlpath;
1082 :     my $ce = $r->ce;
1083 :     my $root = $ce->{webworkURLs}->{root};
1084 :     my $courseName = $urlpath->arg("courseID");
1085 : sh002i 1594
1086 :     my $editMode = $options{editMode};
1087 :     my $userSelected = $options{userSelected};
1088 : toenail 2109
1089 :     my $statusClass = $ce->{siteDefaults}->{status}->{$User->{status}};
1090 : sh002i 1594
1091 : gage 1938 my $changeEUserURL = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}),
1092 :     params => {effectiveUser => $User->user_id}
1093 :     );
1094 : sh002i 1594
1095 : gage 1938 my $setsAssignedToUserURL = $self->systemLink($urlpath->new(type=>'instructor_sets_assigned_to_user',
1096 :     args=>{courseID => $courseName,
1097 :     userID => $User->user_id
1098 :     }),
1099 :     params => {effectiveUser => $User->user_id}
1100 :     );
1101 : sh002i 1640
1102 : sh002i 1594 my @tableCells;
1103 :    
1104 :     # Select
1105 :     if ($editMode) {
1106 :     # column not there
1107 :     } else {
1108 :     # selection checkbox
1109 :     push @tableCells, CGI::checkbox(
1110 :     -name => "selected_users",
1111 :     -value => $User->user_id,
1112 :     -checked => $userSelected,
1113 :     -label => "",
1114 :     );
1115 :     }
1116 :    
1117 : sh002i 1640 # Act As
1118 :     if ($editMode) {
1119 :     # column not there
1120 :     } else {
1121 :     # selection checkbox
1122 : gage 1736 push @tableCells, CGI::a({href=>$changeEUserURL}, $User->user_id);
1123 : sh002i 1640 }
1124 :    
1125 : sh002i 1594 # User ID
1126 :     if ($editMode) {
1127 :     # straight user ID
1128 : toenail 2109 push @tableCells, CGI::div({class=>$statusClass}, $User->user_id);
1129 : sh002i 1594 } else {
1130 : sh002i 1640 # "edit sets assigned to user" link
1131 : gage 1753 push @tableCells, CGI::a({href=>$setsAssignedToUserURL}, "Edit sets");
1132 : sh002i 1594 }
1133 : toenail 2109
1134 : sh002i 1594 # User Fields
1135 :     foreach my $field ($User->NONKEYFIELDS) {
1136 :     my $fieldName = "user." . $User->user_id . "." . $field,
1137 :     my $fieldValue = $User->$field;
1138 :     my %properties = %{ FIELD_PROPERTIES()->{$field} };
1139 :     $properties{access} = "readonly" unless $editMode;
1140 : sh002i 1973 $fieldValue = $self->nbsp($fieldValue) unless $editMode;
1141 : toenail 2109 push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties));
1142 : sh002i 1594 }
1143 :    
1144 :     # PermissionLevel Fields
1145 :     foreach my $field ($PermissionLevel->NONKEYFIELDS) {
1146 :     my $fieldName = "permission." . $PermissionLevel->user_id . "." . $field,
1147 :     my $fieldValue = $PermissionLevel->$field;
1148 :     my %properties = %{ FIELD_PROPERTIES()->{$field} };
1149 :     $properties{access} = "readonly" unless $editMode;
1150 : sh002i 1973 $fieldValue = $self->nbsp($fieldValue) unless $editMode;
1151 : toenail 2109 push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties));
1152 : sh002i 1594 }
1153 :    
1154 :     return CGI::Tr({}, CGI::td({}, \@tableCells));
1155 :     }
1156 :    
1157 :     sub printTableHTML {
1158 :     my ($self, $UsersRef, $PermissionLevelsRef, $fieldNamesRef, %options) = @_;
1159 : gage 1928 my $r = $self->r;
1160 :     my $userTemplate = $self->{userTemplate};
1161 : sh002i 1594 my $permissionLevelTemplate = $self->{permissionLevelTemplate};
1162 : gage 1928 my @Users = @$UsersRef;
1163 :     my @PermissionLevels = @$PermissionLevelsRef;
1164 :     my %fieldNames = %$fieldNamesRef;
1165 : sh002i 1594
1166 : gage 1928 my $editMode = $options{editMode};
1167 :     my %selectedUserIDs = map { $_ => 1 } @{ $options{selectedUserIDs} };
1168 :     my $currentSort = $options{currentSort};
1169 : sh002i 1594
1170 :     # names of headings:
1171 :     my @realFieldNames = (
1172 :     $userTemplate->KEYFIELDS,
1173 :     $userTemplate->NONKEYFIELDS,
1174 :     $permissionLevelTemplate->NONKEYFIELDS,
1175 : malsyned 1233 );
1176 :    
1177 : sh002i 1594 my %sortSubs = %{ SORT_SUBS() };
1178 :     #my @stateParams = @{ STATE_PARAMS() };
1179 :     #my $hrefPrefix = $r->uri . "?" . $self->url_args(@stateParams); # $self->url_authen_args
1180 :     my @tableHeadings;
1181 :     foreach my $field (@realFieldNames) {
1182 : gage 1938 my $result = $fieldNames{$field};
1183 : sh002i 1594 push @tableHeadings, $result;
1184 :     };
1185 :    
1186 :     # prepend selection checkbox? only if we're NOT editing!
1187 : sh002i 1640 unshift @tableHeadings, "Select", "Act As" unless $editMode;
1188 : sh002i 1594
1189 :     # print the table
1190 :     if ($editMode) {
1191 :     print CGI::start_table({});
1192 :     } else {
1193 :     print CGI::start_table({-border=>1});
1194 :     }
1195 :    
1196 :     print CGI::Tr({}, CGI::th({}, \@tableHeadings));
1197 :    
1198 : gage 1713
1199 : sh002i 1594 for (my $i = 0; $i < @Users; $i++) {
1200 :     my $User = $Users[$i];
1201 :     my $PermissionLevel = $PermissionLevels[$i];
1202 :    
1203 :     print $self->recordEditHTML($User, $PermissionLevel,
1204 :     editMode => $editMode,
1205 :     userSelected => exists $selectedUserIDs{$User->user_id}
1206 :     );
1207 :     }
1208 :    
1209 :     print CGI::end_table();
1210 : gage 1713 #########################################
1211 :     # if there are no users shown print message
1212 :     #
1213 :     ##########################################
1214 :    
1215 :     print CGI::p(
1216 :     CGI::i("No students shown. Choose one of the options above to
1217 :     list the students in the course.")
1218 :     ) unless @Users;
1219 : sh002i 1594 }
1220 :    
1221 :     1;
1222 :    

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9