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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 1584 Revision 1594
11WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific 11WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific
12data editing 12data editing
13 13
14=cut 14=cut
15 15
16=for comment
17
18What do we want to be able to do here?
19
20Filter what users are shown:
21 - none, all, selected
22 - matching user_id, matching section, matching recitation
23Switch from view mode to edit mode:
24 - showing visible users
25 - showing selected users
26Switch from edit mode to view and save changes
27Switch from edit mode to view and abandon changes
28Delete users:
29 - visible
30 - selected
31Import users:
32 - replace:
33 - any users
34 - visible users
35 - selected users
36 - no users
37 - add:
38 - any users
39 - no users
40Export users:
41 - export:
42 - all
43 - visible
44 - selected
45 - to:
46 - existing file on server (overwrite): [ list of files ]
47 - new file on server (create): [ filename ]
48 - client
49
50=cut
51
16use strict; 52use strict;
17use warnings; 53use warnings;
18use CGI qw(); 54use CGI qw();
55use WeBWorK::Utils qw(readFile readDirectory);
19 56
20use constant HIDE_USERS_THRESHHOLD => 20; 57use constant HIDE_USERS_THRESHHOLD => 20;
58use constant EDIT_FORMS => [qw(cancelEdit saveEdit)];
59use constant VIEW_FORMS => [qw(filter edit delete import export)];
60use constant STATE_PARAMS => [qw(user effectiveUser key visible_users no_visible_users prev_visible_users no_prev_visible_users editMode sortField)];
61
62use constant SORT_SUBS => {
63 user_id => \&byUserID,
64 first_name => \&byFirstName,
65 last_name => \&byLastName,
66 email_address => \&byEmailAddress,
67 student_id => \&byStudentID,
68 status => \&byStatus,
69 section => \&bySection,
70 recitation => \&byRecitation,
71 comment => \&byComment,
72};
73
74use constant FIELD_PROPERTIES => {
75 user_id => {
76 type => "text",
77 size => 8,
78 access => "readonly",
79 },
80 first_name => {
81 type => "text",
82 size => 10,
83 access => "readwrite",
84 },
85 last_name => {
86 type => "text",
87 size => 10,
88 access => "readwrite",
89 },
90 email_address => {
91 type => "text",
92 size => 20,
93 access => "readwrite",
94 },
95 student_id => {
96 type => "text",
97 size => 11,
98 access => "readwrite",
99 },
100 status => {
101 type => "enumerable",
102 size => 4,
103 access => "readwrite",
104 items => {
105 "C" => "Enrolled",
106 "D" => "Drop",
107 "A" => "Audit",
108 },
109 synonyms => {
110 qr/^[ce]/i => "C",
111 qr/^[dw]/i => "D",
112 qr/^a/i => "A",
113 "*" => "C",
114 }
115 },
116 section => {
117 type => "text",
118 size => 4,
119 access => "readwrite",
120 },
121 recitation => {
122 type => "text",
123 size => 4,
124 access => "readwrite",
125 },
126 comment => {
127 type => "text",
128 size => 20,
129 access => "readwrite",
130 },
131 permission => {
132 type => "number",
133 size => 2,
134 access => "readwrite",
135 }
136};
21 137
22sub initialize { 138sub initialize {
23 my ($self) = @_; 139 my ($self) = @_;
24 my $r = $self->{r}; 140 my $r = $self->{r};
25 my $db = $self->{db}; 141 my $db = $self->{db};
31 $self->{submitError} = "You are not authorized to modify student data"; 147 $self->{submitError} = "You are not authorized to modify student data";
32 return; 148 return;
33 } 149 }
34 150
35 if (defined($r->param('save_classlist'))) { 151 if (defined($r->param('save_classlist'))) {
36 my @userList = $db->listUsers;
37 foreach my $user (@userList) {
38 my $userRecord = $db->getUser($user);
39 my $permissionLevelRecord = $db->getPermissionLevel($user);
40 foreach my $field ($userRecord->NONKEYFIELDS()) {
41 my $paramName = "user.${user}.${field}";
42 if (defined($r->param($paramName))) {
43 $userRecord->$field($r->param($paramName));
44 }
45 }
46 foreach my $field ($permissionLevelRecord->NONKEYFIELDS()) {
47 my $paramName = "permission.${user}.${field}";
48 if (defined($r->param($paramName))) {
49 $permissionLevelRecord->$field($r->param($paramName));
50 }
51 }
52 $db->putUser($userRecord);
53 $db->putPermissionLevel($permissionLevelRecord);
54 }
55 } elsif (defined($r->param('delete_selected'))) { 152 } elsif (defined($r->param('delete_selected'))) {
56 foreach my $userID ($r->param('selectUser')) {
57 $db->deleteUser($userID);
58 }
59 } elsif (defined($r->param('addStudent'))) { 153 } elsif (defined($r->param('addStudent'))) {
60 my $newUser = $db->newUser; 154 my $newUser = $db->newUser;
61 my $newPermissionLevel = $db->newPermissionLevel; 155 my $newPermissionLevel = $db->newPermissionLevel;
62 my $newPassword = $db->newPassword; 156 my $newPassword = $db->newPassword;
63 $newUser->user_id($r->param('newUserID')); 157 $newUser->user_id($r->param('newUserID'));
69 $db->addPermissionLevel($newPermissionLevel); 163 $db->addPermissionLevel($newPermissionLevel);
70 $db->addPassword($newPassword); 164 $db->addPassword($newPassword);
71 } 165 }
72} 166}
73 167
74sub fieldEditHTML {
75 my ($self, $fieldName, $value, $properties) = @_;
76 my $size = $properties->{size};
77 my $type = $properties->{type};
78 my $access = $properties->{access};
79 my $items = $properties->{items};
80 my $synonyms = $properties->{synonyms};
81
82 if ($access eq "readonly") {
83 return $value;
84 }
85
86 if ($type eq "number" or $type eq "text") {
87 return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size});
88 }
89
90 if ($type eq "enumerable") {
91 my $matched = undef; # Whether a synonym match has occurred
92
93 # Process synonyms for enumerable objects
94 foreach my $synonym (keys %$synonyms) {
95 if ($synonym ne "*" and $value =~ m/$synonym/) {
96 $value = $synonyms->{$synonym};
97 $matched = 1;
98 }
99 }
100
101 if (!$matched and exists $synonyms->{"*"}) {
102 $value = $synonyms->{"*"};
103 }
104
105 return CGI::popup_menu({
106 name => $fieldName,
107 values => [keys %$items],
108 default => $value,
109 labels => $items,
110 });
111 }
112}
113
114# generate labels for section/recitation popup menus
115sub menuLabels {
116 my ($self, $hashRef) = @_;
117 my %hash = %$hashRef;
118
119 my %result;
120 foreach my $key (keys %hash) {
121 my $count = @{ $hash{$key} };
122 my $displayKey = $key || "<none>";
123 $result{$key} = "$displayKey ($count users)";
124 }
125 return %result;
126}
127
128sub title { 168sub title {
129 my $self = shift; 169 my $self = shift;
130 return "User List"; 170 return "User List";
131} 171}
132 172
153 my $db = $self->{db}; 193 my $db = $self->{db};
154 my $ce = $self->{ce}; 194 my $ce = $self->{ce};
155 my $root = $ce->{webworkURLs}->{root}; 195 my $root = $ce->{webworkURLs}->{root};
156 my $courseName = $ce->{courseName}; 196 my $courseName = $ce->{courseName};
157 197
198 # templates for getting field names
199 my $userTemplate = $self->{userTemplate} = $db->newUser;
200 my $permissionLevelTemplate = $self->{permissionLevelTemplate} = $db->newPermissionLevel;
201
158 return CGI::em("You are not authorized to access the Instructor tools.") 202 return CGI::em("You are not authorized to access the Instructor tools.")
159 unless $authz->hasPermissions($user, "access_instructor_tools"); 203 unless $authz->hasPermissions($user, "access_instructor_tools");
160 204
161 my $userTemplate = $db->newUser;
162 my $permissionLevelTemplate = $db->newPermissionLevel;
163
164 my $editMode = 0;
165 if (defined $r->param("edit_selected") or defined $r->param("edit_visible")) {
166 $editMode = 1;
167 }
168
169 # This table can be consulted when display-ready forms of field names are needed. 205 # This table can be consulted when display-ready forms of field names are needed.
170 my %prettyFieldNames = map { $_ => $_ } ( 206 my %prettyFieldNames = map { $_ => $_ }
171 $userTemplate->FIELDS(), $permissionLevelTemplate->FIELDS()); 207 $userTemplate->FIELDS(),
208 $permissionLevelTemplate->FIELDS();
172 209
173 @prettyFieldNames{qw( 210 @prettyFieldNames{qw(
174 user_id 211 user_id
175 first_name 212 first_name
176 last_name 213 last_name
192 "Recitation", 229 "Recitation",
193 "Comment", 230 "Comment",
194 "Perm. Level" 231 "Perm. Level"
195 ); 232 );
196 233
197 my %fieldProperties = ( 234 ########## set initial values for state fields
198 user_id => { 235
199 type => "text", 236 my @allUserIDs = $db->listUsers;
200 size => 8, 237 $self->{allUserIDs} = \@allUserIDs;
201 access => "readonly", 238
239 if (defined $r->param("visible_users")) {
240 $self->{visibleUserIDs} = [ $r->param("visible_users") ];
241 } elsif (defined $r->param("no_visible_users")) {
242 $self->{visibleUserIDs} = [];
243 } else {
244 $self->{visibleUserIDs} = [ @allUserIDs ];
245 }
246
247 $self->{prevVisibleUserIDs} = $self->{visibleUserIDs};
248
249 if (defined $r->param("selected_users")) {
250 $self->{selectedUserIDs} = [ $r->param("selected_users") ];
251 } else {
252 $self->{selectedUserIDs} = [];
253 }
254
255 $self->{editMode} = $r->param("editMode") || 0;
256
257 $self->{sortField} = $r->param("sortField") || "last_name";
258
259 ########## call action handler
260
261 my $actionID = $r->param("action");
262 if ($actionID) {
263 unless (grep { $_ eq $actionID } @{ VIEW_FORMS() }, @{ EDIT_FORMS() }) {
264 die "Action $actionID not found";
202 }, 265 }
203 first_name => { 266 my $actionHandler = "${actionID}_handler";
204 type => "text", 267 my %genericParams;
205 size => 10, 268 foreach my $param (qw(selected_users)) {
206 access => $editMode ? "readwrite" : "readonly", 269 $genericParams{$param} = [ $r->param($param) ];
207 }, 270 }
208 last_name => { 271 my %actionParams = $self->getActionParams($actionID);
209 type => "text", 272 my %tableParams = $self->getTableParams();
210 size => 10, 273 print CGI::p(
211 access => $editMode ? "readwrite" : "readonly", 274 "Action: ",
275 CGI::i($self->$actionHandler(\%genericParams, \%actionParams, \%tableParams))
276 );
277 }
278
279 ########## retrieve possibly changed values for member fields
280
281 #@allUserIDs = @{ $self->{allUserIDs} }; # do we need this one?
282 my @visibleUserIDs = @{ $self->{visibleUserIDs} };
283 my @prevVisibleUserIDs = @{ $self->{prevVisibleUserIDs} };
284 my @selectedUserIDs = @{ $self->{selectedUserIDs} };
285 my $editMode = $self->{editMode};
286 my $sortField = $self->{sortField};
287
288 #warn "visibleUserIDs=@visibleUserIDs\n";
289 #warn "prevVisibleUserIDs=@prevVisibleUserIDs\n";
290 #warn "selectedUserIDs=@selectedUserIDs\n";
291 #warn "editMode=$editMode\n";
292
293 ########## get required users
294
295 my @Users = @visibleUserIDs ? $db->getUsers(@visibleUserIDs) : ();
296
297 # presort users
298 my %sortSubs = %{ SORT_SUBS() };
299 my $sortSub = $sortSubs{$sortField};
300 #@Users = sort $sortSub @Users;
301 @Users = sort byLnFnUid @Users;
302
303 my @PermissionLevels;
304
305 for (my $i = 0; $i < @Users; $i++) {
306 my $User = $Users[$i];
307 my $PermissionLevel = $db->getPermissionLevel($User->user_id);
308
309 unless ($PermissionLevel) {
310 # uh oh! no permission level record found!
311 warn "added missing permission level for user ", $User->user_id, "\n";
312
313 # create a new permission level record
314 $PermissionLevel = $db->newPermissionLevel;
315 $PermissionLevel->user_id($User->user_id);
316 $PermissionLevel->permission(0);
317
318 # add it to the database
319 $db->addPermissionLevel($PermissionLevel);
212 }, 320 }
213 email_address => { 321
214 type => "text", 322 $PermissionLevels[$i] = $PermissionLevel;
215 size => 20, 323 }
216 access => $editMode ? "readwrite" : "readonly", 324
217 }, 325 ########## print beginning of form
218 student_id => { 326
219 type => "text", 327 print CGI::start_form({method=>"post", action=>$r->uri, name=>"userlist"});
220 size => 11, 328 print $self->hidden_authen_fields();
221 access => $editMode ? "readwrite" : "readonly", 329
222 }, 330 ########## print state data
223 status => { 331
224 type => "enumerable", 332 print "\n<!-- state data here -->\n";
225 size => 4, 333
226 access => $editMode ? "readwrite" : "readonly", 334 if (@visibleUserIDs) {
227 items => { 335 print CGI::hidden(-name=>"visible_users", -value=>\@visibleUserIDs);
228 "C" => "Enrolled", 336 } else {
229 "D" => "Drop", 337 print CGI::hidden(-name=>"no_visible_users", -value=>"1");
230 "A" => "Audit", 338 }
339
340 if (@prevVisibleUserIDs) {
341 print CGI::hidden(-name=>"prev_visible_users", -value=>\@prevVisibleUserIDs);
342 } else {
343 print CGI::hidden(-name=>"no_prev_visible_users", -value=>"1");
344 }
345
346 print CGI::hidden(-name=>"editMode", -value=>$editMode);
347
348 print CGI::hidden(-name=>"sortField", -value=>$sortField);
349
350 print "\n<!-- state data here -->\n";
351
352 ########## print table
353
354 print CGI::p("Showing ", scalar @visibleUserIDs, " out of ", scalar @allUserIDs, " users.");
355
356 $self->printTableHTML(\@Users, \@PermissionLevels, \%prettyFieldNames,
357 editMode => $editMode,
358 selectedUserIDs => \@selectedUserIDs,
359 );
360
361 ########## print action forms
362
363 print CGI::start_table({});
364 print CGI::Tr({}, CGI::td({-colspan=>2}, "Select an action to perform:"));
365
366 my @formsToShow;
367 if ($editMode) {
368 @formsToShow = @{ EDIT_FORMS() };
369 } else {
370 @formsToShow = @{ VIEW_FORMS() };
371 }
372
373 my $i = 0;
374 foreach my $actionID (@formsToShow) {
375 my $actionForm = "${actionID}_form";
376 my $onChange = "document.userlist.action[$i].checked=true";
377 my %actionParams = $self->getActionParams($actionID);
378
379 print CGI::Tr({-valign=>"top"},
380 CGI::td({}, CGI::input({-type=>"radio", -name=>"action", -value=>$actionID})),
381 CGI::td({}, $self->$actionForm($onChange, %actionParams))
382 );
383
384 $i++;
385 }
386
387 print CGI::Tr({}, CGI::td({-colspan=>2, -align=>"center"},
388 CGI::submit(-value=>"Take Action!"))
389 );
390 print CGI::end_table();
391
392 ########## print end of form
393
394 print CGI::end_form();
395
396 return "";
397}
398
399################################################################################
400# extract particular params and put them in a hash (values are ARRAYREFs!)
401################################################################################
402
403sub getActionParams {
404 my ($self, $actionID) = @_;
405 my $r = $self->{r};
406
407 my %actionParams;
408 foreach my $param ($r->param) {
409 next unless $param =~ m/^action\.$actionID\./;
410 $actionParams{$param} = [ $r->param($param) ];
411 }
412 return %actionParams;
413}
414
415sub getTableParams {
416 my ($self) = @_;
417 my $r = $self->{r};
418
419 my %tableParams;
420 foreach my $param ($r->param) {
421 next unless $param =~ m/^(?:user|permission)\./;
422 $tableParams{$param} = [ $r->param($param) ];
423 }
424 return %tableParams;
425}
426
427################################################################################
428# actions and action triggers
429################################################################################
430
431sub filter_form {
432 my ($self, $onChange, %actionParams) = @_;
433 return join("",
434 "Show ",
435 CGI::popup_menu(
436 -name => "action.filter.scope",
437 -values => [qw(all none selected)],
438 -default => $actionParams{"action.filter.scope"}->[0] || "selected",
439 -labels => {
440 all => "all users",
441 none => "no users",
442 selected => "selected users"
231 }, 443 },
232 synonyms => { 444 -onchange => $onChange,
233 qr/^[ce]/i => "C", 445 ),
234 qr/^[dw]/i => "D", 446 );
235 qr/^a/i => "A", 447}
236 "*" => "C", 448
449# this action handler modifies the "visibleUserIDs" field based on the contents
450# of the "action.filter.scope" parameter and the "selected_users"
451sub filter_handler {
452 my ($self, $genericParams, $actionParams, $tableParams) = @_;
453
454 my $result;
455
456 my $scope = $actionParams->{"action.filter.scope"}->[0];
457 if ($scope eq "all") {
458 $result = "showing all users";
459 $self->{visibleUserIDs} = $self->{allUserIDs};
460 } elsif ($scope eq "none") {
461 $result = "showing no users";
462 $self->{visibleUserIDs} = [];
463 } elsif ($scope eq "selected") {
464 $result = "showing selected users";
465 $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
466 }
467
468 return $result;
469}
470
471sub edit_form {
472 my ($self, $onChange, %actionParams) = @_;
473 return join("",
474 "Edit ",
475 CGI::popup_menu(
476 -name => "action.edit.scope",
477 -values => [qw(all visible selected)],
478 -default => $actionParams{"action.edit.scope"}->[0] || "selected",
479 -labels => {
480 all => "all users",
481 visible => "visible users",
482 selected => "selected users"
483 },
484 -onchange => $onChange,
485 ),
486 );
487}
488
489sub edit_handler {
490 my ($self, $genericParams, $actionParams, $tableParams) = @_;
491
492 my $result;
493
494 my $scope = $actionParams->{"action.edit.scope"}->[0];
495 if ($scope eq "all") {
496 $result = "editing all users";
497 $self->{visibleUserIDs} = $self->{allUserIDs};
498 } elsif ($scope eq "visible") {
499 $result = "editing visible users";
500 # leave visibleUserIDs alone
501 } elsif ($scope eq "selected") {
502 $result = "editing selected users";
503 $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
504 }
505 $self->{editMode} = 1;
506
507 return $result;
508}
509
510sub delete_form {
511 my ($self, $onChange, %actionParams) = @_;
512 return join("",
513 "Delete ",
514 CGI::popup_menu(
515 -name => "action.delete.scope",
516 -values => [qw(all visible selected)],
517 -default => $actionParams{"action.delete.scope"}->[0] || "selected",
518 -labels => {
519 all => "all users",
520 visible => "visible users",
521 selected => "selected users"
522 },
523 -onchange => $onChange,
524 ),
525 CGI::em(" Deletion is not undoable!"),
526 CGI::br(),
527 CGI::em("Deleting a user destroys the user-specific set and problem data for that user."),
528 );
529}
530
531sub delete_handler {
532 my ($self, $genericParams, $actionParams, $tableParams) = @_;
533 my $db = $self->{db};
534 my $scope = $actionParams->{"action.delete.scope"}->[0];
535
536 my @userIDsToDelete;
537 if ($scope eq "all") {
538 @userIDsToDelete = @{ $self->{allUserIDs} };
539 } elsif ($scope eq "visible") {
540 @userIDsToDelete = @{ $self->{visibleUserIDs} };
541 } elsif ($scope eq "selected") {
542 @userIDsToDelete = @{ $self->{selectedUserIDs} };
543 }
544
545 my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
546 my %visibleUserIDs = map { $_ => 1 } @{ $self->{visibleUserIDs} };
547 my %selectedUserIDs = map { $_ => 1 } @{ $self->{selectedUserIDs} };
548
549 foreach my $userID (@userIDsToDelete) {
550 delete $allUserIDs{$userID};
551 delete $visibleUserIDs{$userID};
552 delete $selectedUserIDs{$userID};
553 $db->deleteUser($userID);
554 }
555
556 $self->{allUserIDs} = [ keys %allUserIDs ];
557 $self->{visibleUserIDs} = [ keys %visibleUserIDs ];
558 $self->{selectedUserIDs} = [ keys %selectedUserIDs ];
559
560 my $num = @userIDsToDelete;
561 return "deleted $num user" . ($num == 1 ? "" : "s");
562}
563
564sub import_form {
565 my ($self, $onChange, %actionParams) = @_;
566 return join(" ",
567 "Import users from file",
568 CGI::popup_menu(
569 -name => "action.import.source",
570 -values => [ "", $self->getCSVList() ],
571 -default => $actionParams{"action.import.source"}->[0] || "",
572 -onchange => $onChange,
573 ), CGI::br(),
574 "replacing",
575 CGI::popup_menu(
576 -name => "action.import.replace",
577 -values => [qw(any visible selected none)],
578 -default => $actionParams{"action.import.replace"}->[0] || "none",
579 -labels => {
580 any => "any",
581 visible => "visible",
582 selected => "selected",
583 none => "no",
584 },
585 -onchange => $onChange,
586 ),
587 "existing users", CGI::br(),
588 " and adding",
589 CGI::popup_menu(
590 -name => "action.import.add",
591 -values => [qw(any none)],
592 -default => $actionParams{"action.import.add"}->[0] || "any",
593 -labels => {
594 any => "any",
595 none => "no",
596 },
597 -onchange => $onChange,
598 ),
599 "new users",
600 );
601}
602
603sub import_handler {
604 my ($self, $genericParams, $actionParams, $tableParams) = @_;
605
606 my $source = $actionParams->{"action.import.source"}->[0];
607 my $add = $actionParams->{"action.import.add"}->[0];
608 my $replace = $actionParams->{"action.import.replace"}->[0];
609
610 my $fileName = $source;
611 my $createNew = $add eq "any";
612 my $replaceExisting;
613 my @replaceList;
614 if ($replace eq "any") {
615 $replaceExisting = "any";
616 } elsif ($replace eq "none") {
617 $replaceExisting = "none";
618 } elsif ($replace eq "visible") {
619 $replaceExisting = "listed";
620 @replaceList = @{ $self->{visibleUserIDs} };
621 } elsif ($replace eq "selected") {
622 $replaceExisting = "listed";
623 @replaceList = @{ $self->{selectedUserIDs} };
624 }
625
626 my ($replaced, $added, $skipped)
627 = $self->importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList);
628
629 my $numReplaced = @$replaced;
630 my $numAdded = @$added;
631 my $numSkipped = @$skipped;
632
633 return $numReplaced . " user" . ($numReplaced == 1 ? "" : "s") . " replaced, "
634 . $numAdded . " user" . ($numAdded == 1 ? "" : "s") . " added, "
635 . $numSkipped . " user" . ($numSkipped == 1 ? "" : "s") . " skipped.";
636}
637
638sub export_form {
639 my ($self, $onChange, %actionParams) = @_;
640 return join(" ",
641 "Export",
642 CGI::popup_menu(
643 -name => "action.export.scope",
644 -values => [qw(all visible selected)],
645 -default => $actionParams{"action.export.scope"}->[0] || "selected",
646 -labels => {
647 all => "all users",
648 visible => "visible users",
649 selected => "selected users"
650 },
651 -onchange => $onChange,
652 ),
653 " -- not yet implemented",
654 CGI::br(),
655 CGI::input({
656 -type => "radio",
657 -name => "action.export.target",
658 -value => "existing",
659 -onchange => $onChange,
660 }),
661 " Overwrite the existing classlist file named ",
662 CGI::popup_menu(
663 -name=>"action.export.existingName",
664 -values => [ "", $self->getCSVList() ],
665 -default => $actionParams{"action.export.existingName"}->[0] || "",
666 ), CGI::br(),
667 CGI::input({
668 -type => "radio",
669 -name => "action.export.target",
670 -value => "new",
671 -onchange => $onChange,
672 }),
673 " Create a new classlist file named ",
674 CGI::textfield(
675 -name => "action.export.newName",
676 -value => "",
677 -width => "31",
678 ),
679 );
680}
681
682sub cancelEdit_form {
683 my ($self, $onChange, %actionParams) = @_;
684 return "Abandon changes";
685}
686
687sub cancelEdit_handler {
688 my ($self, $genericParams, $actionParams, $tableParams) = @_;
689 my $r = $self->{r};
690
691 #$self->{selectedUserIDs} = $self->{visibleUserIDs};
692 # only do the above if we arrived here via "edit selected users"
693 if (defined $r->param("prev_visible_users")) {
694 $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
695 } elsif (defined $r->param("no_prev_visible_users")) {
696 $self->{visibleUserIDs} = [];
697 } else {
698 # leave it alone
699 }
700 $self->{editMode} = 0;
701
702 return "changes abandoned";
703}
704
705sub saveEdit_form {
706 my ($self, $onChange, %actionParams) = @_;
707 return "Save changes";
708}
709
710sub saveEdit_handler {
711 my ($self, $genericParams, $actionParams, $tableParams) = @_;
712 my $r = $self->{r};
713 my $db = $self->{db};
714
715 my @visibleUserIDs = @{ $self->{visibleUserIDs} };
716 foreach my $userID (@visibleUserIDs) {
717 my $User = $db->getUser($userID);
718 my $PermissionLevel = $db->getPermissionLevel($userID);
719
720 foreach my $field ($User->NONKEYFIELDS()) {
721 my $param = "user.${userID}.${field}";
722 if (defined $tableParams->{$param}->[0]) {
723 $User->$field($tableParams->{$param}->[0]);
237 } 724 }
238 }, 725 }
239 section => { 726
240 type => "text", 727 foreach my $field ($PermissionLevel->NONKEYFIELDS()) {
241 size => 4, 728 my $param = "permission.${userID}.${field}";
242 access => $editMode ? "readwrite" : "readonly", 729 if (defined $tableParams->{$param}->[0]) {
243 }, 730 $PermissionLevel->$field($tableParams->{$param}->[0]);
244 recitation => {
245 type => "text",
246 size => 4,
247 access => $editMode ? "readwrite" : "readonly",
248 },
249 comment => {
250 type => "text",
251 size => 20,
252 access => $editMode ? "readwrite" : "readonly",
253 },
254 permission => {
255 type => "number",
256 size => 2,
257 access => $editMode ? "readwrite" : "readonly",
258 } 731 }
732 }
733
734 $db->putUser($User);
735 $db->putPermissionLevel($PermissionLevel);
736 }
737
738 if (defined $r->param("prev_visible_users")) {
739 $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
740 } elsif (defined $r->param("no_prev_visible_users")) {
741 $self->{visibleUserIDs} = [];
742 } else {
743 # leave it alone
744 }
745
746 $self->{editMode} = 0;
747
748 return "changes saved";
749}
750
751################################################################################
752# sorts
753################################################################################
754
755sub byUserID { $a->user_id cmp $b->user_id }
756sub byFirstName { $a->first_name cmp $b->first_name }
757sub byLastName { $a->last_name cmp $b->last_name }
758sub byEmailAddress { $a->email_address cmp $b->email_address }
759sub byStudentID { $a->student_id cmp $b->student_id }
760sub byStatus { $a->status cmp $b->status }
761sub bySection { $a->section cmp $b->section }
762sub byRecitation { $a->recitation cmp $b->recitation }
763sub byComment { $a->comment cmp $b->comment }
764
765sub byLnFnUid { &byLastName || &byFirstName || &byUserID }
766
767################################################################################
768# utilities
769################################################################################
770
771# generate labels for section/recitation popup menus
772sub menuLabels {
773 my ($self, $hashRef) = @_;
774 my %hash = %$hashRef;
775
776 my %result;
777 foreach my $key (keys %hash) {
778 my $count = @{ $hash{$key} };
779 my $displayKey = $key || "<none>";
780 $result{$key} = "$displayKey ($count users)";
781 }
782 return %result;
783}
784
785sub getCSVList {
786 my ($self) = @_;
787 my $ce = $self->{ce};
788 my $dir = $ce->{courseDirs}->{templates};
789 return grep { not m/^\./ and m/\.lst$/ and -f "$dir/$_" } readDirectory($dir);
790}
791
792sub importUsersFromCSV {
793 my ($self, $fileName, $createNew, $replaceExisting, @replaceList) = @_;
794 my $ce = $self->{ce};
795 my $db = $self->{db};
796 my $dir = $ce->{courseDirs}->{templates};
797
798 die "illegal character in input: \"/\"" if $fileName =~ m|/|;
799 die "won't be able to read from file $dir/$fileName: does it exist? is it readable?"
800 unless -r "$dir/$fileName";
801
802 my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
803 my %replaceOK;
804 if ($replaceExisting eq "none") {
805 %replaceOK = ();
806 } elsif ($replaceExisting eq "listed") {
807 %replaceOK = map { $_ => 1 } @replaceList;
808 } elsif ($replaceExisting eq "any") {
809 %replaceOK = %allUserIDs;
810 }
811
812 my (@replaced, @added, @skipped);
813
814 my @contents = split /\n/, readFile("$dir/$fileName");
815 foreach my $string (@contents) {
816 $string =~ s/^\s+//;
817 $string =~ s/\s+$//;
818 my (
819 $student_id, $last_name, $first_name, $status, $comment,
820 $section, $recitation, $email_address, $user_id
821 ) = split /\s*,\s*/, $string;
822
823 if (exists $allUserIDs{$user_id} and not exists $replaceOK{$user_id}) {
824 push @skipped, $user_id;
825 next;
826 }
827
828 if (not exists $allUserIDs{$user_id} and not $createNew) {
829 push @skipped, $user_id;
830 next;
831 }
832
833 my $User = $db->newUser;
834 $User->user_id($user_id);
835 $User->first_name($first_name);
836 $User->last_name($last_name);
837 $User->email_address($email_address);
838 $User->student_id($student_id);
839 $User->status($status);
840 $User->section($section);
841 $User->recitation($recitation);
842 $User->comment($comment);
843
844 my $PermissionLevel = $db->newPermissionLevel;
845 $PermissionLevel->user_id($user_id);
846 $PermissionLevel->permission(0);
847
848 my $Password = $db->newPassword;
849 $Password->user_id($user_id);
850 $Password->password($student_id);
851
852 if (exists $allUserIDs{$user_id}) {
853 $db->putUser($User);
854 $db->putPermissionLevel($PermissionLevel);
855 $db->putPassword($Password);
856 push @replaced, $user_id;
857 } else {
858 $db->addUser($User);
859 $db->addPermissionLevel($PermissionLevel);
860 $db->addPassword($Password);
861 push @added, $user_id;
862 }
863 }
864
865 return \@replaced, \@added, \@skipped;
866}
867
868################################################################################
869# "display" methods
870################################################################################
871
872sub fieldEditHTML {
873 my ($self, $fieldName, $value, $properties) = @_;
874 my $size = $properties->{size};
875 my $type = $properties->{type};
876 my $access = $properties->{access};
877 my $items = $properties->{items};
878 my $synonyms = $properties->{synonyms};
879
880 if ($access eq "readonly") {
881 return $value;
882 }
883
884 if ($type eq "number" or $type eq "text") {
885 return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size});
886 }
887
888 if ($type eq "enumerable") {
889 my $matched = undef; # Whether a synonym match has occurred
890
891 # Process synonyms for enumerable objects
892 foreach my $synonym (keys %$synonyms) {
893 if ($synonym ne "*" and $value =~ m/$synonym/) {
894 $value = $synonyms->{$synonym};
895 $matched = 1;
896 }
897 }
898
899 if (!$matched and exists $synonyms->{"*"}) {
900 $value = $synonyms->{"*"};
901 }
902
903 return CGI::popup_menu({
904 name => $fieldName,
905 values => [keys %$items],
906 default => $value,
907 labels => $items,
908 });
909 }
910}
911
912sub recordEditHTML {
913 my ($self, $User, $PermissionLevel, %options) = @_;
914 my $r = $self->{r};
915 my $ce = $self->{ce};
916 my $root = $ce->{webworkURLs}->{root};
917 my $courseName = $ce->{courseName};
918
919 my $editMode = $options{editMode};
920 my $userSelected = $options{userSelected};
921
922 my $changeEUserURL = "$root/$courseName?"
923 . "user=" . $r->param("user")
924 . "&effectiveUser=" . $User->user_id
925 . "&key=" . $r->param("key");
926
927 my @tableCells;
928
929 # Select
930 if ($editMode) {
931 # column not there
932 } else {
933 # selection checkbox
934 push @tableCells, CGI::checkbox(
935 -name => "selected_users",
936 -value => $User->user_id,
937 -checked => $userSelected,
938 -label => "",
939 );
940 }
941
942 # User ID
943 if ($editMode) {
944 # straight user ID
945 push @tableCells, $User->user_id;
946 } else {
947 # "become user" link
948 push @tableCells, CGI::a({href=>$changeEUserURL}, $User->user_id);
949 }
950
951 # User Fields
952 foreach my $field ($User->NONKEYFIELDS) {
953 my $fieldName = "user." . $User->user_id . "." . $field,
954 my $fieldValue = $User->$field;
955 my %properties = %{ FIELD_PROPERTIES()->{$field} };
956 $properties{access} = "readonly" unless $editMode;
957 push @tableCells, $self->fieldEditHTML($fieldName, $fieldValue, \%properties);
958 }
959
960 # PermissionLevel Fields
961 foreach my $field ($PermissionLevel->NONKEYFIELDS) {
962 my $fieldName = "permission." . $PermissionLevel->user_id . "." . $field,
963 my $fieldValue = $PermissionLevel->$field;
964 my %properties = %{ FIELD_PROPERTIES()->{$field} };
965 $properties{access} = "readonly" unless $editMode;
966 push @tableCells, $self->fieldEditHTML($fieldName, $fieldValue, \%properties);
967 }
968
969 return CGI::Tr({}, CGI::td({}, \@tableCells));
970}
971
972sub printTableHTML {
973 my ($self, $UsersRef, $PermissionLevelsRef, $fieldNamesRef, %options) = @_;
974 my $r = $self->{r};
975 my $userTemplate = $self->{userTemplate};
976 my $permissionLevelTemplate = $self->{permissionLevelTemplate};
977 my @Users = @$UsersRef;
978 my @PermissionLevels = @$PermissionLevelsRef;
979 my %fieldNames = %$fieldNamesRef;
980
981 my $editMode = $options{editMode};
982 my %selectedUserIDs = map { $_ => 1 } @{ $options{selectedUserIDs} };
983 my $currentSort = $options{currentSort};
984
985 # names of headings:
986 my @realFieldNames = (
987 $userTemplate->KEYFIELDS,
988 $userTemplate->NONKEYFIELDS,
989 $permissionLevelTemplate->NONKEYFIELDS,
259 ); 990 );
991
992 my %sortSubs = %{ SORT_SUBS() };
993 #my @stateParams = @{ STATE_PARAMS() };
994 #my $hrefPrefix = $r->uri . "?" . $self->url_args(@stateParams); # $self->url_authen_args
995 my @tableHeadings;
996 foreach my $field (@realFieldNames) {
997 my $result;
998 #if (exists $sortSubs{$field}) {
999 # $result = CGI::a({-href=>"$hrefPrefix&sort=$field"}, $fieldNames{$field});
1000 #} else {
1001 $result = $fieldNames{$field};
1002 #}
1003 push @tableHeadings, $result;
1004 };
1005
1006 # prepend selection checkbox? only if we're NOT editing!
1007 unshift @tableHeadings, "Sel." unless $editMode;
1008
1009 # print the table
1010 if ($editMode) {
1011 print CGI::start_table({});
1012 } else {
1013 print CGI::start_table({-border=>1});
1014 }
1015
1016 print CGI::Tr({}, CGI::th({}, \@tableHeadings));
1017
1018 for (my $i = 0; $i < @Users; $i++) {
1019 my $User = $Users[$i];
1020 my $PermissionLevel = $PermissionLevels[$i];
1021
1022 print $self->recordEditHTML($User, $PermissionLevel,
1023 editMode => $editMode,
1024 userSelected => exists $selectedUserIDs{$User->user_id}
1025 );
1026 }
1027
1028 print CGI::end_table();
1029}
1030
10311;
1032
1033__END__
1034
1035 my $editMode = 0;
1036 if (defined $r->param("edit_selected") or defined $r->param("edit_visible")) {
1037 $editMode = 1;
1038 }
260 1039
261 my @userIDs = $db->listUsers; 1040 my @userIDs = $db->listUsers;
262 my @userRecords = $db->getUsers(@userIDs); 1041 my @userRecords = $db->getUsers(@userIDs);
263 1042
264 my (%sections, %recitations); 1043 my (%sections, %recitations);
316 } @userRecords; 1095 } @userRecords;
317 1096
318 print CGI::start_form({method=>"post", action=>$r->uri()}); 1097 print CGI::start_form({method=>"post", action=>$r->uri()});
319 print $self->hidden_authen_fields(); 1098 print $self->hidden_authen_fields();
320 1099
321 # filter options 1100 filter options
322 my %labels = ( 1101 my %labels = (
323 none => "No users", 1102 none => "No users",
324 all => "All " . scalar @userIDs . " users", 1103 all => "All " . scalar @userIDs . " users",
325 filter_selected => "Users selected below", 1104 filter_selected => "Users selected below",
326 filter_user_id => "User with ID " . CGI::input({ 1105 filter_user_id => "User with ID " . CGI::input({
472 print CGI::submit({name=>"addStudent", value=>"Add User"}); 1251 print CGI::submit({name=>"addStudent", value=>"Add User"});
473 print CGI::end_form(); 1252 print CGI::end_form();
474 } 1253 }
475 1254
476 return ""; 1255 return "";
477}
478
4791;

Legend:
Removed from v.1584  
changed lines
  Added in v.1594

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9