[system] / branches / ghe3_dev / webwork2 / lib / WeBWorK / ContentGenerator / Instructor / UserList.pm Repository:
ViewVC logotype

Annotation of /branches/ghe3_dev/webwork2/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm

Parent Directory Parent Directory | Revision Log Revision Log


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

1 : sh002i 1567 ################################################################################
2 : sh002i 1663 # WeBWorK Online Homework Delivery System
3 : sh002i 5319 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
4 : gage 6270 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm,v 1.96 2010/05/14 00:52:48 gage 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 : ghe3 6819 use base qw(WeBWorK);
19 : malsyned 832 use base qw(WeBWorK::ContentGenerator::Instructor);
20 :    
21 :     =head1 NAME
22 :    
23 : sh002i 1567 WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific
24 :     data editing
25 : malsyned 832
26 :     =cut
27 :    
28 : sh002i 1594 =for comment
29 :    
30 :     What do we want to be able to do here?
31 :    
32 :     Filter what users are shown:
33 :     - none, all, selected
34 :     - matching user_id, matching section, matching recitation
35 :     Switch from view mode to edit mode:
36 :     - showing visible users
37 :     - showing selected users
38 :     Switch from edit mode to view and save changes
39 :     Switch from edit mode to view and abandon changes
40 : apizer 3287 Switch from view mode to password mode:
41 :     - showing visible users
42 :     - showing selected users
43 :     Switch from password mode to view and save changes
44 :     Switch from password mode to view and abandon changes
45 : sh002i 1594 Delete users:
46 :     - visible
47 :     - selected
48 :     Import users:
49 :     - replace:
50 :     - any users
51 :     - visible users
52 :     - selected users
53 :     - no users
54 :     - add:
55 :     - any users
56 :     - no users
57 :     Export users:
58 :     - export:
59 :     - all
60 :     - visible
61 :     - selected
62 :     - to:
63 :     - existing file on server (overwrite): [ list of files ]
64 :     - new file on server (create): [ filename ]
65 :    
66 :     =cut
67 :    
68 : malsyned 832 use strict;
69 :     use warnings;
70 : gage 4234 #use CGI qw(-nosticky );
71 :     use WeBWorK::CGI;
72 : sh002i 2884 use WeBWorK::File::Classlist;
73 : gage 6270 use WeBWorK::DB qw(check_user_id);
74 : gage 2137 use WeBWorK::Utils qw(readFile readDirectory cryptPassword);
75 : apizer 3304 use constant HIDE_USERS_THRESHHOLD => 200;
76 : sh002i 1594 use constant EDIT_FORMS => [qw(cancelEdit saveEdit)];
77 : apizer 3287 use constant PASSWORD_FORMS => [qw(cancelPassword savePassword)];
78 :     use constant VIEW_FORMS => [qw(filter sort edit password import export add delete)];
79 : toenail 2355
80 :     # permissions needed to perform a given action
81 :     use constant FORM_PERMS => {
82 :     saveEdit => "modify_student_data",
83 :     edit => "modify_student_data",
84 : apizer 3287 savePassword => "change_password",
85 :     password => "change_password",
86 : toenail 2355 import => "modify_student_data",
87 :     export => "modify_classlist_files",
88 :     add => "modify_student_data",
89 :     delete => "modify_student_data",
90 :     };
91 :    
92 :     # permissions needed to view a given field
93 :     use constant FIELD_PERMS => {
94 :     act_as => "become_student",
95 :     sets => "assign_problem_sets",
96 :     };
97 :    
98 : apizer 3312 use constant STATE_PARAMS => [qw(user effectiveUser key visible_users no_visible_users prev_visible_users no_prev_visible_users editMode passwordMode primarySortField secondarySortField ternarySortField labelSortMethod)];
99 : sh002i 1567
100 : sh002i 1594 use constant SORT_SUBS => {
101 :     user_id => \&byUserID,
102 :     first_name => \&byFirstName,
103 :     last_name => \&byLastName,
104 :     email_address => \&byEmailAddress,
105 :     student_id => \&byStudentID,
106 :     status => \&byStatus,
107 :     section => \&bySection,
108 :     recitation => \&byRecitation,
109 :     comment => \&byComment,
110 : apizer 3299 permission => \&byPermission,
111 : sh002i 1594 };
112 :    
113 :     use constant FIELD_PROPERTIES => {
114 :     user_id => {
115 :     type => "text",
116 :     size => 8,
117 :     access => "readonly",
118 :     },
119 :     first_name => {
120 :     type => "text",
121 :     size => 10,
122 :     access => "readwrite",
123 :     },
124 :     last_name => {
125 :     type => "text",
126 :     size => 10,
127 :     access => "readwrite",
128 :     },
129 :     email_address => {
130 :     type => "text",
131 :     size => 20,
132 :     access => "readwrite",
133 :     },
134 :     student_id => {
135 :     type => "text",
136 :     size => 11,
137 :     access => "readwrite",
138 :     },
139 :     status => {
140 : sh002i 3690 #type => "enumerable",
141 :     type => "status",
142 : sh002i 1594 size => 4,
143 :     access => "readwrite",
144 : sh002i 3690 #items => {
145 :     # "C" => "Enrolled",
146 :     # "D" => "Drop",
147 :     # "A" => "Audit",
148 :     #},
149 :     #synonyms => {
150 :     # qr/^[ce]/i => "C",
151 :     # qr/^[dw]/i => "D",
152 :     # qr/^a/i => "A",
153 :     # "*" => "C",
154 :     #}
155 : sh002i 1594 },
156 :     section => {
157 :     type => "text",
158 :     size => 4,
159 :     access => "readwrite",
160 :     },
161 :     recitation => {
162 :     type => "text",
163 :     size => 4,
164 :     access => "readwrite",
165 :     },
166 :     comment => {
167 :     type => "text",
168 :     size => 20,
169 :     access => "readwrite",
170 :     },
171 :     permission => {
172 : glarose 4913 # this really should be read from $r->ce, but that's not available here
173 :     type => "permission",
174 : sh002i 1594 access => "readwrite",
175 : glarose 4913 # type => "number",
176 :     # size => 2,
177 :     # access => "readwrite",
178 : sh002i 1594 }
179 :     };
180 : gage 1712 sub pre_header_initialize {
181 : gage 1928 my $self = shift;
182 :     my $r = $self->r;
183 :     my $urlpath = $r->urlpath;
184 : toenail 2355 my $authz = $r->authz;
185 : gage 1928 my $ce = $r->ce;
186 :     my $courseName = $urlpath->arg("courseID");
187 : toenail 2355 my $user = $r->param('user');
188 : gage 1712 # Handle redirects, if any.
189 :     ##############################
190 :     # Redirect to the addUser page
191 :     ##################################
192 : toenail 2355
193 :     # Check permissions
194 :     return unless $authz->hasPermissions($user, "access_instructor_tools");
195 : gage 1712
196 :     defined($r->param('action')) && $r->param('action') eq 'add' && do {
197 :     # fix url and redirect
198 :     my $root = $ce->{webworkURLs}->{root};
199 : gage 1928
200 : gage 1712 my $numberOfStudents = $r->param('number_of_students');
201 :     warn "number of students not defined " unless defined $numberOfStudents;
202 : sh002i 1594
203 : gage 2009 my $uri=$self->systemLink( $urlpath->newFromModule('WeBWorK::ContentGenerator::Instructor::AddUsers',courseID=>$courseName),
204 :     params=>{
205 :     number_of_students=>$numberOfStudents,
206 :     }
207 : gage 1938 );
208 : gage 1712 #FIXME does the display mode need to be defined?
209 :     #FIXME url_authen_args also includes an effective user, so the new one must come first.
210 :     # even that might not work with every browser since there are two effective User assignments.
211 : sh002i 4241 $self->reply_with_redirect($uri);
212 : gage 1712 return;
213 :     };
214 :     }
215 : toenail 1799
216 : malsyned 1015 sub initialize {
217 : malsyned 1211 my ($self) = @_;
218 : gage 1928 my $r = $self->r;
219 :     my $db = $r->db;
220 :     my $ce = $r->ce;
221 :     my $authz = $r->authz;
222 : gage 1712 my $user = $r->param('user');
223 : malsyned 1211
224 : toenail 2355 # Check permissions
225 :     return unless $authz->hasPermissions($user, "access_instructor_tools");
226 : malsyned 1211
227 : sh002i 1601 #if (defined($r->param('addStudent'))) {
228 :     # my $newUser = $db->newUser;
229 :     # my $newPermissionLevel = $db->newPermissionLevel;
230 :     # my $newPassword = $db->newPassword;
231 :     # $newUser->user_id($r->param('newUserID'));
232 :     # $newPermissionLevel->user_id($r->param('newUserID'));
233 :     # $newPassword->user_id($r->param('newUserID'));
234 :     # $newUser->status('C');
235 :     # $newPermissionLevel->permission(0);
236 :     # $db->addUser($newUser);
237 :     # $db->addPermissionLevel($newPermissionLevel);
238 :     # $db->addPassword($newPassword);
239 :     #}
240 : malsyned 1015 }
241 :    
242 : gage 1295
243 :    
244 : malsyned 1014 sub body {
245 : gage 1928 my ($self) = @_;
246 :     my $r = $self->r;
247 :     my $urlpath = $r->urlpath;
248 :     my $db = $r->db;
249 :     my $ce = $r->ce;
250 :     my $authz = $r->authz;
251 :     my $courseName = $urlpath->arg("courseID");
252 :     my $setID = $urlpath->arg("setID");
253 :     my $user = $r->param('user');
254 : sh002i 1584
255 : gage 1928 my $root = $ce->{webworkURLs}->{root};
256 :    
257 : sh002i 1594 # templates for getting field names
258 :     my $userTemplate = $self->{userTemplate} = $db->newUser;
259 :     my $permissionLevelTemplate = $self->{permissionLevelTemplate} = $db->newPermissionLevel;
260 :    
261 : toenail 2355 return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to access the instructor tools."))
262 : sh002i 1567 unless $authz->hasPermissions($user, "access_instructor_tools");
263 : sh002i 1584
264 : malsyned 1214 # This table can be consulted when display-ready forms of field names are needed.
265 : sh002i 1594 my %prettyFieldNames = map { $_ => $_ }
266 :     $userTemplate->FIELDS(),
267 :     $permissionLevelTemplate->FIELDS();
268 : sh002i 1567
269 : malsyned 1214 @prettyFieldNames{qw(
270 :     user_id
271 :     first_name
272 :     last_name
273 :     email_address
274 :     student_id
275 :     status
276 :     section
277 :     recitation
278 :     comment
279 :     permission
280 :     )} = (
281 : apizer 3287 "Login Name",
282 : malsyned 1214 "First Name",
283 :     "Last Name",
284 : apizer 3302 "Email Address",
285 : malsyned 1214 "Student ID",
286 :     "Status",
287 :     "Section",
288 :     "Recitation",
289 :     "Comment",
290 : apizer 3299 "Permission Level"
291 : malsyned 1214 );
292 : sh002i 1567
293 : toenail 3060 $self->{prettyFieldNames} = \%prettyFieldNames;
294 : sh002i 1594 ########## set initial values for state fields
295 :    
296 : glarose 4923 # exclude set-level proctors
297 :     my @allUserIDs = grep {$_ !~ /^set_id:/} $db->listUsers;
298 : sh002i 4518 # DBFIXME count would work
299 : toenail 2355 $self->{totalSets} = $db->listGlobalSets; # save for use in "assigned sets" links
300 : sh002i 1594 $self->{allUserIDs} = \@allUserIDs;
301 :    
302 : sh002i 4518 # DBFIXME filter in the database
303 : apizer 3312 if (defined $r->param("visable_user_string")) {
304 :     my @visableUserIDs = split /:/, $r->param("visable_user_string");
305 :     $self->{visibleUserIDs} = [ @visableUserIDs ];
306 :     } elsif (defined $r->param("visible_users")) {
307 :     $self->{visibleUserIDs} = [ $r->param("visible_users") ];
308 : sh002i 1594 } elsif (defined $r->param("no_visible_users")) {
309 :     $self->{visibleUserIDs} = [];
310 :     } else {
311 : apizer 3304 if ((@allUserIDs > HIDE_USERS_THRESHHOLD) and (not defined $r->param("show_all_users") )) {
312 : sh002i 1597 $self->{visibleUserIDs} = [];
313 :     } else {
314 :     $self->{visibleUserIDs} = [ @allUserIDs ];
315 :     }
316 : sh002i 1594 }
317 :    
318 :     $self->{prevVisibleUserIDs} = $self->{visibleUserIDs};
319 :    
320 :     if (defined $r->param("selected_users")) {
321 :     $self->{selectedUserIDs} = [ $r->param("selected_users") ];
322 :     } else {
323 :     $self->{selectedUserIDs} = [];
324 :     }
325 :    
326 :     $self->{editMode} = $r->param("editMode") || 0;
327 : toenail 2355
328 :     return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify student data"))
329 :     if $self->{editMode} and not $authz->hasPermissions($user, "modify_student_data");
330 :    
331 : apizer 3287
332 :     $self->{passwordMode} = $r->param("passwordMode") || 0;
333 :    
334 :     return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify student data"))
335 :     if $self->{passwordMode} and not $authz->hasPermissions($user, "modify_student_data");
336 :    
337 : apizer 3302 if (defined $r->param("labelSortMethod")) {
338 :     $self->{primarySortField} = $r->param("labelSortMethod");
339 :     $self->{secondarySortField} = $r->param("primarySortField");
340 :     $self->{ternarySortField} = $r->param("secondarySortField");
341 :     }
342 :     else {
343 :     $self->{primarySortField} = $r->param("primarySortField") || "last_name";
344 :     $self->{secondarySortField} = $r->param("secondarySortField") || "first_name";
345 :     $self->{ternarySortField} = $r->param("ternarySortField") || "student_id";
346 :     }
347 : sh002i 1594
348 : sh002i 4518 # DBFIXME use an iterator
349 : sh002i 1601 my @allUsers = $db->getUsers(@allUserIDs);
350 :     my (%sections, %recitations);
351 :     foreach my $User (@allUsers) {
352 : sh002i 1973 push @{$sections{defined $User->section ? $User->section : ""}}, $User->user_id;
353 :     push @{$recitations{defined $User->recitation ? $User->recitation : ""}}, $User->user_id;
354 : sh002i 1601 }
355 :     $self->{sections} = \%sections;
356 :     $self->{recitations} = \%recitations;
357 :    
358 : sh002i 1594 ########## call action handler
359 :    
360 :     my $actionID = $r->param("action");
361 :     if ($actionID) {
362 : apizer 3287 unless (grep { $_ eq $actionID } @{ VIEW_FORMS() }, @{ EDIT_FORMS() }, @{ PASSWORD_FORMS() } ) {
363 : sh002i 1594 die "Action $actionID not found";
364 :     }
365 : toenail 2355 # Check permissions
366 :     if (not FORM_PERMS()->{$actionID} or $authz->hasPermissions($user, FORM_PERMS()->{$actionID})) {
367 :     my $actionHandler = "${actionID}_handler";
368 :     my %genericParams;
369 :     foreach my $param (qw(selected_users)) {
370 :     $genericParams{$param} = [ $r->param($param) ];
371 :     }
372 :     my %actionParams = $self->getActionParams($actionID);
373 :     my %tableParams = $self->getTableParams();
374 :     print CGI::p(
375 :     '<div style="color:green">',
376 :     "Result of last action performed: ",
377 :     CGI::i($self->$actionHandler(\%genericParams, \%actionParams, \%tableParams)),
378 :     '</div>',
379 :     CGI::hr()
380 :     );
381 :     } else {
382 :     return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to perform this action."));
383 : sh002i 1594 }
384 :     }
385 :    
386 :     ########## retrieve possibly changed values for member fields
387 :    
388 :     #@allUserIDs = @{ $self->{allUserIDs} }; # do we need this one?
389 : sh002i 4518 # DBFIXME instead of re-listing, why not add added users to $self->{allUserIDs} ?
390 : glarose 4923 # exclude set-level proctors
391 :     @allUserIDs = grep {$_ !~ /^set_id:/} $db->listUsers; # recompute value in case some were added
392 : sh002i 1594 my @visibleUserIDs = @{ $self->{visibleUserIDs} };
393 :     my @prevVisibleUserIDs = @{ $self->{prevVisibleUserIDs} };
394 :     my @selectedUserIDs = @{ $self->{selectedUserIDs} };
395 :     my $editMode = $self->{editMode};
396 : apizer 3287 my $passwordMode = $self->{passwordMode};
397 : toenail 3060 my $primarySortField = $self->{primarySortField};
398 :     my $secondarySortField = $self->{secondarySortField};
399 : apizer 3287 my $ternarySortField = $self->{ternarySortField};
400 : sh002i 1594
401 :     #warn "visibleUserIDs=@visibleUserIDs\n";
402 :     #warn "prevVisibleUserIDs=@prevVisibleUserIDs\n";
403 :     #warn "selectedUserIDs=@selectedUserIDs\n";
404 :     #warn "editMode=$editMode\n";
405 : apizer 3287 #warn "passwordMode=$passwordMode\n";
406 : apizer 3299 #warn "primarySortField=$primarySortField\n";
407 : apizer 3302 #warn "secondarySortField=$secondarySortField\n";
408 :     #warn "ternarySortField=$ternarySortField\n";
409 :    
410 : sh002i 1594 ########## get required users
411 : sh002i 1601
412 :     my @Users = grep { defined $_ } @visibleUserIDs ? $db->getUsers(@visibleUserIDs) : ();
413 : toenail 3060
414 :     my %sortSubs = %{ SORT_SUBS() };
415 :     my $primarySortSub = $sortSubs{$primarySortField};
416 : apizer 3287 my $secondarySortSub = $sortSubs{$secondarySortField};
417 :     my $ternarySortSub = $sortSubs{$ternarySortField};
418 : apizer 3299
419 :     # add permission level to user record hash so we can sort it if necessary
420 : sh002i 4518 # DBFIXME this calls for a join... (i'd like the User record to contain permission level info)
421 : apizer 3299 if ($primarySortField eq 'permission' or $secondarySortField eq 'permission' or $ternarySortField eq 'permission') {
422 :     foreach my $User (@Users) {
423 :     next unless $User;
424 :     my $permissionLevel = $db->getPermissionLevel($User->user_id);
425 :     $User->{permission} = $permissionLevel->permission;
426 :     }
427 :     }
428 : apizer 3287
429 : sh002i 1594
430 : apizer 3287 # # don't forget to sort in opposite order of importance
431 :     # @Users = sort $secondarySortSub @Users;
432 :     # @Users = sort $primarySortSub @Users;
433 :     # #@Users = sort byLnFnUid @Users;
434 :    
435 :     # Always have a definite sort order even if first three sorts don't determine things
436 :     @Users = sort {
437 :     &$primarySortSub
438 :     ||
439 :     &$secondarySortSub
440 :     ||
441 :     &$ternarySortSub
442 :     ||
443 :     byLastName
444 :     ||
445 :     byFirstName
446 :     ||
447 :     byUserID
448 :     }
449 :     @Users;
450 : sh002i 1594
451 :     my @PermissionLevels;
452 :    
453 :     for (my $i = 0; $i < @Users; $i++) {
454 :     my $User = $Users[$i];
455 : sh002i 4518 # DBFIX we maybe already have the permission level from above (for use in sorting)
456 : gage 1667 my $PermissionLevel = $db->getPermissionLevel($User->user_id); # checked
457 : sh002i 1594
458 : sh002i 4518 # DBFIXME this should go in the DB layer
459 : sh002i 1594 unless ($PermissionLevel) {
460 :     # uh oh! no permission level record found!
461 :     warn "added missing permission level for user ", $User->user_id, "\n";
462 :    
463 :     # create a new permission level record
464 :     $PermissionLevel = $db->newPermissionLevel;
465 :     $PermissionLevel->user_id($User->user_id);
466 :     $PermissionLevel->permission(0);
467 :    
468 :     # add it to the database
469 :     $db->addPermissionLevel($PermissionLevel);
470 :     }
471 :    
472 :     $PermissionLevels[$i] = $PermissionLevel;
473 :     }
474 :    
475 :     ########## print beginning of form
476 :    
477 : ghe3 6827 print CGI::start_form({method=>"post", action=>$self->systemLink($urlpath,authen=>0), name=>"userlist", id=>"classlist_edit_form"});
478 : sh002i 1594 print $self->hidden_authen_fields();
479 :    
480 :     ########## print state data
481 :    
482 :     print "\n<!-- state data here -->\n";
483 :    
484 :     if (@visibleUserIDs) {
485 : apizer 3312 print CGI::hidden(-name=>"visible_users", -value=>\@visibleUserIDs);
486 : sh002i 1594 } else {
487 :     print CGI::hidden(-name=>"no_visible_users", -value=>"1");
488 :     }
489 :    
490 :     if (@prevVisibleUserIDs) {
491 :     print CGI::hidden(-name=>"prev_visible_users", -value=>\@prevVisibleUserIDs);
492 :     } else {
493 :     print CGI::hidden(-name=>"no_prev_visible_users", -value=>"1");
494 :     }
495 :    
496 :     print CGI::hidden(-name=>"editMode", -value=>$editMode);
497 :    
498 : apizer 3287 print CGI::hidden(-name=>"passwordMode", -value=>$passwordMode);
499 :    
500 : toenail 3060 print CGI::hidden(-name=>"primarySortField", -value=>$primarySortField);
501 : apizer 3287 print CGI::hidden(-name=>"secondarySortField", -value=>$secondarySortField);
502 :     print CGI::hidden(-name=>"ternarySortField", -value=>$ternarySortField);
503 : sh002i 1594
504 :     print "\n<!-- state data here -->\n";
505 :    
506 :     ########## print action forms
507 :    
508 : ghe3 6827 # print CGI::start_table({});
509 :     print CGI::p("Select an action to perform:");
510 : sh002i 1594
511 :     my @formsToShow;
512 :     if ($editMode) {
513 :     @formsToShow = @{ EDIT_FORMS() };
514 : apizer 3287 }elsif ($passwordMode) {
515 :     @formsToShow = @{ PASSWORD_FORMS() };
516 : sh002i 1594 } else {
517 :     @formsToShow = @{ VIEW_FORMS() };
518 :     }
519 :    
520 :     my $i = 0;
521 :     foreach my $actionID (@formsToShow) {
522 : toenail 2355 # Check permissions
523 :     next if FORM_PERMS()->{$actionID} and not $authz->hasPermissions($user, FORM_PERMS()->{$actionID});
524 : sh002i 1594 my $actionForm = "${actionID}_form";
525 :     my $onChange = "document.userlist.action[$i].checked=true";
526 :     my %actionParams = $self->getActionParams($actionID);
527 :    
528 : ghe3 6827 print CGI::div({-class=>"column"},WeBWorK::CGI_labeled_input(-type=>"radio", -id=>$actionID."_id", -label_text=>ucfirst(split_cap($actionID)), -input_attr=>{-name=>"action", -value=>$actionID}, -label_attr=>{-class=>"radio_label"}),CGI::br(),$self->$actionForm($onChange, %actionParams),CGI::br());
529 : sh002i 1594
530 :     $i++;
531 :     }
532 : ghe3 6827 my $selectAll =WeBWorK::CGI_labeled_input(-type=>'button', -id=>"select_all", -input_attr=>{-name=>'check_all', -class=>"button_input", -value=>'Select all users',
533 : gage 6088 onClick => "for (i in document.userlist.elements) {
534 :     if (document.userlist.elements[i].name =='selected_users') {
535 :     document.userlist.elements[i].checked = true
536 :     }
537 :     }" });
538 : ghe3 6827 my $selectNone =WeBWorK::CGI_labeled_input(-type=>'button', -id=>"select_none", -input_attr=>{-name=>'check_none', -class=>"button_input", -value=>'Unselect all users',
539 : gage 6088 onClick => "for (i in document.userlist.elements) {
540 :     if (document.userlist.elements[i].name =='selected_users') {
541 :     document.userlist.elements[i].checked = false
542 :     }
543 :     }" });
544 :     unless ($editMode or $passwordMode) {
545 : ghe3 6827 print $selectAll." ". $selectNone;
546 : gage 6088 }
547 : ghe3 6827 print WeBWorK::CGI_labeled_input(-type=>"reset", -id=>"clear_entries", -input_attr=>{-value=>"Clear", -class=>"button_input"});
548 :     print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"take_action", -input_attr=>{-value=>"Take Action!", -class=>"button_input"}).CGI::br().CGI::br();
549 :     # print CGI::end_table();
550 : sh002i 1594
551 : sh002i 1597 ########## print table
552 :    
553 : gage 4258 print CGI::p({},"Showing ", scalar @Users, " out of ", scalar @allUserIDs, " users.");
554 : sh002i 1597
555 : apizer 3287 print CGI::p("If a password field is left blank, the student's current password will be maintained.") if $passwordMode;
556 : gage 3856 if ($editMode) {
557 :    
558 :    
559 :     print CGI::p('<b>Click</b> on the login name to <b>edit individual problem set data</b>, (e.g. due dates) for these students.');
560 :     }
561 : sh002i 1597 $self->printTableHTML(\@Users, \@PermissionLevels, \%prettyFieldNames,
562 :     editMode => $editMode,
563 : apizer 3287 passwordMode => $passwordMode,
564 : sh002i 1597 selectedUserIDs => \@selectedUserIDs,
565 : apizer 3302 primarySortField => $primarySortField,
566 :     secondarySortField => $secondarySortField,
567 :     visableUserIDs => \@visibleUserIDs,
568 : sh002i 1597 );
569 :    
570 :    
571 : sh002i 1594 ########## print end of form
572 :    
573 :     print CGI::end_form();
574 :    
575 :     return "";
576 :     }
577 :    
578 :     ################################################################################
579 :     # extract particular params and put them in a hash (values are ARRAYREFs!)
580 :     ################################################################################
581 :    
582 :     sub getActionParams {
583 :     my ($self, $actionID) = @_;
584 :     my $r = $self->{r};
585 :    
586 :     my %actionParams;
587 :     foreach my $param ($r->param) {
588 :     next unless $param =~ m/^action\.$actionID\./;
589 :     $actionParams{$param} = [ $r->param($param) ];
590 :     }
591 :     return %actionParams;
592 :     }
593 :    
594 :     sub getTableParams {
595 :     my ($self) = @_;
596 :     my $r = $self->{r};
597 :    
598 :     my %tableParams;
599 :     foreach my $param ($r->param) {
600 :     next unless $param =~ m/^(?:user|permission)\./;
601 :     $tableParams{$param} = [ $r->param($param) ];
602 :     }
603 :     return %tableParams;
604 :     }
605 :    
606 :     ################################################################################
607 :     # actions and action triggers
608 :     ################################################################################
609 :    
610 : sh002i 1597 # filter, edit, cancelEdit, and saveEdit should stay with the display module and
611 :     # not be real "actions". that way, all actions are shown in view mode and no
612 :     # actions are shown in edit mode.
613 :    
614 : sh002i 1594 sub filter_form {
615 :     my ($self, $onChange, %actionParams) = @_;
616 : sh002i 1601 #return CGI::table({}, CGI::Tr({-valign=>"top"},
617 :     # CGI::td({},
618 : toenail 3060
619 :     my %prettyFieldNames = %{ $self->{prettyFieldNames} };
620 :    
621 : sh002i 1601 return join("",
622 : ghe3 6819 # "Show ",
623 :     # CGI::popup_menu(
624 :     # -name => "action.filter.scope",
625 :     # -values => [qw(all none selected match_regex)],
626 :     # -default => $actionParams{"action.filter.scope"}->[0] || "match_regex",
627 :     # -labels => {
628 :     # all => "all users",
629 :     # none => "no users",
630 :     # selected => "selected users",
631 :     # # match_ids => "users with matching user IDs:",
632 :     # match_regex => "users who match:",
633 :     # # match_section => "users in selected section",
634 :     # # match_recitation => "users in selected recitation",
635 :     # },
636 :     # -onchange => $onChange,
637 :     # ),
638 :     WeBWorK::CGI_labeled_input(
639 :     -type=>"select",
640 :     -id=>"filter_select",
641 : ghe3 6827 -label_text=>"Show Which Users?: ",
642 : ghe3 6819 -input_attr=>{
643 :     -name => "action.filter.scope",
644 :     -values => [qw(all none selected match_regex)],
645 :     -default => $actionParams{"action.filter.scope"}->[0] || "match_regex",
646 :     -labels => {
647 :     all => "all users",
648 :     none => "no users",
649 :     selected => "selected users",
650 :     match_regex => "users who match:",
651 :     },
652 :     -onchange => $onChange,
653 :     }
654 : sh002i 1601 ),
655 : ghe3 6827 CGI::br(),
656 : sh002i 1601 " ",
657 : ghe3 6819 WeBWorK::CGI_labeled_input(
658 :     -type=>"text",
659 :     -id=>"filter_text",
660 : ghe3 6827 -label_text=>"Matching on: ",
661 : ghe3 6819 -input_attr=>{
662 :     -name => "action.filter.user_ids",
663 :     -value => $actionParams{"action.filter.user_ids"}->[0] || "",,
664 :     -width => "50",
665 :     -onchange => $onChange,
666 :     }
667 : sh002i 1601 ),
668 : ghe3 6827 CGI::br(),
669 : ghe3 6819 # CGI::textfield(
670 :     # -name => "action.filter.user_ids",
671 :     # -value => $actionParams{"action.filter.user_ids"}->[0] || "",,
672 :     # -width => "50",
673 :     # -onchange => $onChange,
674 :     # ),
675 : toenail 3060 # " (separate multiple IDs with commas)",
676 :     # CGI::br(),
677 :     # "sections: ",
678 :     # CGI::popup_menu(
679 :     # -name => "action.filter.section",
680 :     # -values => [ keys %{ $self->{sections} } ],
681 :     # -default => $actionParams{"action.filter.section"}->[0] || "",
682 :     # -labels => { $self->menuLabels($self->{sections}) },
683 :     # -onchange => $onChange,
684 :     # ),
685 :     # " recitations: ",
686 :     # CGI::popup_menu(
687 :     # -name => "action.filter.recitation",
688 :     # -values => [ keys %{ $self->{recitations} } ],
689 :     # -default => $actionParams{"action.filter.recitation"}->[0] || "",
690 :     # -labels => { $self->menuLabels($self->{recitations}) },
691 :     # -onchange => $onChange,
692 :     # ),
693 : ghe3 6819 # " in their ",
694 :     # CGI::popup_menu(
695 :     # -name => "action.filter.field",
696 :     # -value => [ keys %{ FIELD_PROPERTIES() } ],
697 :     # -default => $actionParams{"action.filter.field"}->[0] || "user_id",
698 :     # -labels => \%prettyFieldNames,
699 :     # -onchange => $onChange,
700 :     # ),
701 :     WeBWorK::CGI_labeled_input(
702 :     -type=>"select",
703 :     -id=>"filter_type_select",
704 :     -label_text=>" In their: ",
705 :     -input_attr=>{
706 :     -name => "action.filter.field",
707 :     -value => [ keys %{ FIELD_PROPERTIES() } ],
708 :     -default => $actionParams{"action.filter.field"}->[0] || "user_id",
709 :     -labels => \%prettyFieldNames,
710 :     -onchange => $onChange,
711 :     }
712 : sh002i 1601 ),
713 : sh002i 1594 );
714 : sh002i 1601 # ),
715 :     #));
716 : sh002i 1594 }
717 :    
718 :     # this action handler modifies the "visibleUserIDs" field based on the contents
719 :     # of the "action.filter.scope" parameter and the "selected_users"
720 : sh002i 4518 # DBFIXME filtering should happen in the database!
721 : sh002i 1594 sub filter_handler {
722 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
723 :    
724 : toenail 3060 my $r = $self->r;
725 :     my $db = $r->db;
726 :    
727 : sh002i 1594 my $result;
728 :    
729 :     my $scope = $actionParams->{"action.filter.scope"}->[0];
730 :     if ($scope eq "all") {
731 :     $result = "showing all users";
732 :     $self->{visibleUserIDs} = $self->{allUserIDs};
733 :     } elsif ($scope eq "none") {
734 :     $result = "showing no users";
735 :     $self->{visibleUserIDs} = [];
736 :     } elsif ($scope eq "selected") {
737 :     $result = "showing selected users";
738 :     $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
739 : toenail 3060 } elsif ($scope eq "match_regex") {
740 :     $result = "showing matching users";
741 :     my $regex = $actionParams->{"action.filter.user_ids"}->[0];
742 :     my $field = $actionParams->{"action.filter.field"}->[0];
743 :     my @userRecords = $db->getUsers(@{$self->{allUserIDs}});
744 :     my @userIDs;
745 :     foreach my $record (@userRecords) {
746 :     next unless $record;
747 : toenail 3089
748 :     # add permission level to user record hash so we can match it if necessary
749 :     if ($field eq "permission") {
750 :     my $permissionLevel = $db->getPermissionLevel($record->user_id);
751 :     $record->{permission} = $permissionLevel->permission;
752 :     }
753 : toenail 3060 push @userIDs, $record->user_id if $record->{$field} =~ /^$regex/i;
754 :     }
755 :     $self->{visibleUserIDs} = \@userIDs;
756 : sh002i 1601 } elsif ($scope eq "match_ids") {
757 :     my @userIDs = split /\s*,\s*/, $actionParams->{"action.filter.user_ids"}->[0];
758 :     $self->{visibleUserIDs} = \@userIDs;
759 :     } elsif ($scope eq "match_section") {
760 :     my $section = $actionParams->{"action.filter.section"}->[0];
761 : sh002i 1604 $self->{visibleUserIDs} = $self->{sections}->{$section}; # an arrayref
762 : sh002i 1601 } elsif ($scope eq "match_recitation") {
763 :     my $recitation = $actionParams->{"action.filter.recitation"}->[0];
764 : sh002i 1604 $self->{visibleUserIDs} = $self->{recitations}->{$recitation}; # an arrayref
765 : sh002i 1594 }
766 :    
767 :     return $result;
768 :     }
769 :    
770 : toenail 3060 sub sort_form {
771 :     my ($self, $onChange, %actionParams) = @_;
772 :     return join ("",
773 : ghe3 6819 # "Sort by ",
774 :     # CGI::popup_menu(
775 :     # -name => "action.sort.primary",
776 :     # -values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)],
777 :     # -default => $actionParams{"action.sort.primary"}->[0] || "last_name",
778 :     # -labels => {
779 :     # user_id => "Login Name",
780 :     # first_name => "First Name",
781 :     # last_name => "Last Name",
782 :     # email_address => "Email Address",
783 :     # student_id => "Student ID",
784 :     # status => "Enrollment Status",
785 :     # section => "Section",
786 :     # recitation => "Recitation",
787 :     # comment => "Comment",
788 :     # permission => "Permission Level"
789 :     # },
790 :     # -onchange => $onChange,
791 :     # ),
792 :     WeBWorK::CGI_labeled_input(
793 :     -type=>"select",
794 :     -id=>"sort_select_1",
795 :     -label_text=>"Sort by: ",
796 :     -input_attr=>{
797 :     -name => "action.sort.primary",
798 :     -values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)],
799 :     -default => $actionParams{"action.sort.primary"}->[0] || "last_name",
800 :     -labels => {
801 :     user_id => "Login Name",
802 :     first_name => "First Name",
803 :     last_name => "Last Name",
804 :     email_address => "Email Address",
805 :     student_id => "Student ID",
806 :     status => "Enrollment Status",
807 :     section => "Section",
808 :     recitation => "Recitation",
809 :     comment => "Comment",
810 :     permission => "Permission Level"
811 :     },
812 :     -onchange => $onChange,
813 : toenail 3060 },
814 :     ),
815 : ghe3 6827 CGI::br(),
816 : ghe3 6819 # ", then by ",
817 :     # CGI::popup_menu(
818 :     # -name => "action.sort.secondary",
819 :     # -values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)],
820 :     # -default => $actionParams{"action.sort.secondary"}->[0] || "first_name",
821 :     # -labels => {
822 :     # user_id => "Login Name",
823 :     # first_name => "First Name",
824 :     # last_name => "Last Name",
825 :     # email_address => "Email Address",
826 :     # student_id => "Student ID",
827 :     # status => "Enrollment Status",
828 :     # section => "Section",
829 :     # recitation => "Recitation",
830 :     # comment => "Comment",
831 :     # permission => "Permission Level"
832 :     # },
833 :     # -onchange => $onChange,
834 :     # ),
835 :     WeBWorK::CGI_labeled_input(
836 :     -type=>"select",
837 :     -id=>"sort_select_2",
838 :     -label_text=>"Then by: ",
839 :     -input_attr=>{
840 :     -name => "action.sort.secondary",
841 :     -values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)],
842 :     -default => $actionParams{"action.sort.secondary"}->[0] || "first_name",
843 :     -labels => {
844 :     user_id => "Login Name",
845 :     first_name => "First Name",
846 :     last_name => "Last Name",
847 :     email_address => "Email Address",
848 :     student_id => "Student ID",
849 :     status => "Enrollment Status",
850 :     section => "Section",
851 :     recitation => "Recitation",
852 :     comment => "Comment",
853 :     permission => "Permission Level"
854 :     },
855 :     -onchange => $onChange,
856 : toenail 3060 },
857 :     ),
858 : ghe3 6827 CGI::br(),
859 : ghe3 6819 # ", then by ",
860 :     # CGI::popup_menu(
861 :     # -name => "action.sort.ternary",
862 :     # -values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)],
863 :     # -default => $actionParams{"action.sort.ternary"}->[0] || "user_id",
864 :     # -labels => {
865 :     # user_id => "Login Name",
866 :     # first_name => "First Name",
867 :     # last_name => "Last Name",
868 :     # email_address => "Email Address",
869 :     # student_id => "Student ID",
870 :     # status => "Enrollment Status",
871 :     # section => "Section",
872 :     # recitation => "Recitation",
873 :     # comment => "Comment",
874 :     # permission => "Permission Level"
875 :     # },
876 :     # -onchange => $onChange,
877 :     # ),
878 :     WeBWorK::CGI_labeled_input(
879 :     -type=>"select",
880 :     -id=>"sort_select_3",
881 :     -label_text=>"Then by: ",
882 :     -input_attr=>{
883 :     -name => "action.sort.ternary",
884 :     -values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)],
885 :     -default => $actionParams{"action.sort.ternary"}->[0] || "user_id",
886 :     -labels => {
887 :     user_id => "Login Name",
888 :     first_name => "First Name",
889 :     last_name => "Last Name",
890 :     email_address => "Email Address",
891 :     student_id => "Student ID",
892 :     status => "Enrollment Status",
893 :     section => "Section",
894 :     recitation => "Recitation",
895 :     comment => "Comment",
896 :     permission => "Permission Level"
897 :     },
898 :     -onchange => $onChange,
899 : apizer 3287 },
900 :     ),
901 : toenail 3060 );
902 :     }
903 :    
904 :     sub sort_handler {
905 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
906 :    
907 :     my $primary = $actionParams->{"action.sort.primary"}->[0];
908 :     my $secondary = $actionParams->{"action.sort.secondary"}->[0];
909 : apizer 3287 my $ternary = $actionParams->{"action.sort.ternary"}->[0];
910 : toenail 3060
911 :     $self->{primarySortField} = $primary;
912 :     $self->{secondarySortField} = $secondary;
913 : apizer 3287 $self->{ternarySortField} = $ternary;
914 : toenail 3060
915 :     my %names = (
916 :     user_id => "Login Name",
917 :     first_name => "First Name",
918 :     last_name => "Last Name",
919 : apizer 3302 email_address => "Email Address",
920 : toenail 3060 student_id => "Student ID",
921 :     status => "Enrollment Status",
922 :     section => "Section",
923 :     recitation => "Recitation",
924 :     comment => "Comment",
925 : apizer 3299 permission => "Permission Level"
926 : toenail 3060 );
927 :    
928 : apizer 3287 return "Users sorted by $names{$primary}, then by $names{$secondary}, then by $names{$ternary}.";
929 : toenail 3060 }
930 :    
931 : sh002i 1594 sub edit_form {
932 :     my ($self, $onChange, %actionParams) = @_;
933 : toenail 2355
934 : sh002i 1594 return join("",
935 : ghe3 6819 # "Edit ",
936 :     # CGI::popup_menu(
937 :     # -name => "action.edit.scope",
938 :     # -values => [qw(all visible selected)],
939 :     # -default => $actionParams{"action.edit.scope"}->[0] || "selected",
940 :     # -labels => {
941 :     # all => "all users",
942 :     # visible => "visible users",
943 :     # selected => "selected users"
944 :     # },
945 :     # -onchange => $onChange,
946 :     # ),
947 :     WeBWorK::CGI_labeled_input(
948 :     -type=>"select",
949 :     -id=>"edit_select",
950 : ghe3 6827 -label_text=>"Edit Which Users?: ",
951 : ghe3 6819 -input_attr=>{
952 :     -name => "action.edit.scope",
953 :     -values => [qw(all visible selected)],
954 :     -default => $actionParams{"action.edit.scope"}->[0] || "selected",
955 :     -labels => {
956 :     all => "all users",
957 :     visible => "visible users",
958 :     selected => "selected users"
959 :     },
960 :     -onchange => $onChange,
961 :     }
962 : sh002i 1594 ),
963 :     );
964 :     }
965 :    
966 :     sub edit_handler {
967 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
968 : toenail 2355
969 : sh002i 1594 my $result;
970 :    
971 :     my $scope = $actionParams->{"action.edit.scope"}->[0];
972 :     if ($scope eq "all") {
973 :     $result = "editing all users";
974 :     $self->{visibleUserIDs} = $self->{allUserIDs};
975 :     } elsif ($scope eq "visible") {
976 :     $result = "editing visible users";
977 :     # leave visibleUserIDs alone
978 :     } elsif ($scope eq "selected") {
979 :     $result = "editing selected users";
980 :     $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
981 :     }
982 :     $self->{editMode} = 1;
983 :    
984 :     return $result;
985 :     }
986 :    
987 : apizer 3287
988 :     sub password_form {
989 :     my ($self, $onChange, %actionParams) = @_;
990 :    
991 :     return join("",
992 : ghe3 6822 # "Give new password to ",
993 :     # CGI::popup_menu(
994 :     # -name => "action.password.scope",
995 :     # -values => [qw(all visible selected)],
996 :     # -default => $actionParams{"action.password.scope"}->[0] || "selected",
997 :     # -labels => {
998 :     # all => "all users",
999 :     # visible => "visible users",
1000 :     # selected => "selected users"
1001 :     # },
1002 :     # -onchange => $onChange,
1003 :     # ),
1004 :     WeBWorK::CGI_labeled_input(
1005 :     -type=>"select",
1006 :     -id=>"password_select",
1007 :     -label_text=>"Give new password to: ",
1008 :     -input_attr=>{
1009 :     -name => "action.password.scope",
1010 :     -values => [qw(all visible selected)],
1011 :     -default => $actionParams{"action.password.scope"}->[0] || "selected",
1012 :     -labels => {
1013 :     all => "all users",
1014 :     visible => "visible users",
1015 :     selected => "selected users"
1016 :     },
1017 :     -onchange => $onChange,
1018 : apizer 3287 },
1019 :     ),
1020 :     );
1021 :     }
1022 :    
1023 :     sub password_handler {
1024 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
1025 :    
1026 :     my $result;
1027 :    
1028 :     my $scope = $actionParams->{"action.password.scope"}->[0];
1029 :     if ($scope eq "all") {
1030 :     $result = "giving new passwords to all users";
1031 :     $self->{visibleUserIDs} = $self->{allUserIDs};
1032 :     } elsif ($scope eq "visible") {
1033 :     $result = "giving new passwords to visible users";
1034 :     # leave visibleUserIDs alone
1035 :     } elsif ($scope eq "selected") {
1036 :     $result = "giving new passwords to selected users";
1037 :     $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref
1038 :     }
1039 :     $self->{passwordMode} = 1;
1040 :    
1041 :     return $result;
1042 :     }
1043 :    
1044 : sh002i 1594 sub delete_form {
1045 :     my ($self, $onChange, %actionParams) = @_;
1046 : toenail 2355
1047 : sh002i 1594 return join("",
1048 : ghe3 6822 # CGI::div({class=>"ResultsWithError"},
1049 :     # "Delete ",
1050 :     # CGI::popup_menu(
1051 :     # -name => "action.delete.scope",
1052 :     # -values => [qw(none selected)],
1053 :     # -default => $actionParams{"action.delete.scope"}->[0] || "none",
1054 :     # -labels => {
1055 :     # none => "no users.",
1056 :     # #visible => "visible users.",
1057 :     # selected => "selected users."
1058 :     # },
1059 :     # -onchange => $onChange,
1060 :     # ),
1061 :     # CGI::em(" Deletion destroys all user-related data and is not undoable!"),
1062 :     # ),
1063 : ghe3 6827 CGI::span({-class=>"ResultsWithError"}, CGI::em("Warning: Deletion destroys all user-related data and is not undoable!")),CGI::br(),
1064 : ghe3 6822 WeBWorK::CGI_labeled_input(
1065 :     -type=>"select",
1066 :     -id=>"delete_select",
1067 : ghe3 6827 -label_text=>"Delete how many?: ",
1068 : ghe3 6822 -input_attr=>{
1069 :     -name => "action.delete.scope",
1070 :     -values => [qw(none selected)],
1071 :     -default => $actionParams{"action.delete.scope"}->[0] || "none",
1072 :     -labels => {
1073 :     none => "no users.",
1074 :     # visible => "visible users.",
1075 :     selected => "selected users."
1076 :     },
1077 :     -onchange => $onChange,
1078 : sh002i 1594 },
1079 :     ),
1080 :     );
1081 :     }
1082 :    
1083 :     sub delete_handler {
1084 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
1085 : gage 1928 my $r = $self->r;
1086 :     my $db = $r->db;
1087 : toenail 2355 my $user = $r->param('user');
1088 : sh002i 1594 my $scope = $actionParams->{"action.delete.scope"}->[0];
1089 :    
1090 : gage 1712 my @userIDsToDelete = ();
1091 : sh002i 1782 #if ($scope eq "visible") {
1092 :     # @userIDsToDelete = @{ $self->{visibleUserIDs} };
1093 :     #} elsif ($scope eq "selected") {
1094 :     if ($scope eq "selected") {
1095 : sh002i 1594 @userIDsToDelete = @{ $self->{selectedUserIDs} };
1096 :     }
1097 :    
1098 :     my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
1099 :     my %visibleUserIDs = map { $_ => 1 } @{ $self->{visibleUserIDs} };
1100 :     my %selectedUserIDs = map { $_ => 1 } @{ $self->{selectedUserIDs} };
1101 :    
1102 : toenail 2355 my $error = "";
1103 :     my $num = 0;
1104 : sh002i 1594 foreach my $userID (@userIDsToDelete) {
1105 : toenail 2355 if ($user eq $userID) { # don't delete yourself!!
1106 :     $error = "You cannot delete yourself!";
1107 :     next;
1108 :     }
1109 : sh002i 1594 delete $allUserIDs{$userID};
1110 :     delete $visibleUserIDs{$userID};
1111 :     delete $selectedUserIDs{$userID};
1112 :     $db->deleteUser($userID);
1113 : toenail 2355 $num++;
1114 : sh002i 1594 }
1115 :    
1116 :     $self->{allUserIDs} = [ keys %allUserIDs ];
1117 :     $self->{visibleUserIDs} = [ keys %visibleUserIDs ];
1118 :     $self->{selectedUserIDs} = [ keys %selectedUserIDs ];
1119 :    
1120 : toenail 2355 return "deleted $num user" . ($num == 1 ? "" : "s. ") . $error;
1121 : sh002i 1594 }
1122 : gage 1712 sub add_form {
1123 :     my ($self, $onChange, %actionParams) = @_;
1124 : sh002i 1594
1125 : ghe3 6822 #return "Add ", CGI::input({name=>'number_of_students', value=>1,size => 3}), " student(s). ";
1126 :     return WeBWorK::CGI_labeled_input(-type=>"text", -id=>"add_entry", -label_text=>"Add student(s): ", -input_attr=>{name=>'number_of_students', value=>1,size => 3});
1127 : gage 1712 }
1128 :    
1129 :     sub add_handler {
1130 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
1131 :     # This action is redirected to the addUser.pm module using ../instructor/add_user/...
1132 :     return "Nothing done by add student handler";
1133 :     }
1134 : sh002i 1594 sub import_form {
1135 :     my ($self, $onChange, %actionParams) = @_;
1136 :     return join(" ",
1137 : ghe3 6827 # "Import users from file",
1138 :     # CGI::popup_menu(
1139 :     # -name => "action.import.source",
1140 :     # -values => [ $self->getCSVList() ],
1141 :     # -default => $actionParams{"action.import.source"}->[0] || "",
1142 :     # -onchange => $onChange,
1143 :     # ),
1144 :     WeBWorK::CGI_labeled_input(
1145 :     -type=>"select",
1146 :     -id=>"import_select_source",
1147 :     -label_text=>"Import users from what file?: ",
1148 :     -input_attr=>{
1149 :     -name => "action.import.source",
1150 :     -values => [ $self->getCSVList() ],
1151 :     -default => $actionParams{"action.import.source"}->[0] || "",
1152 :     -onchange => $onChange,
1153 :     }
1154 : sh002i 1597 ),
1155 : ghe3 6827 CGI::br(),
1156 :     # "replacing",
1157 :     # CGI::popup_menu(
1158 :     # -name => "action.import.replace",
1159 :     # -values => [qw(any visible selected none)],
1160 :     # -default => $actionParams{"action.import.replace"}->[0] || "none",
1161 :     # -labels => {
1162 :     # any => "any",
1163 :     # visible => "visible",
1164 :     # selected => "selected",
1165 :     # none => "no",
1166 :     # },
1167 :     # -onchange => $onChange,
1168 :     # ),
1169 :     WeBWorK::CGI_labeled_input(
1170 :     -type=>"select",
1171 :     -id=>"import_select_replace",
1172 :     -label_text=>"Replace which users?: ",
1173 :     -input_attr=>{
1174 :     -name => "action.import.replace",
1175 :     -values => [qw(any visible selected none)],
1176 :     -default => $actionParams{"action.import.replace"}->[0] || "none",
1177 :     -labels => {
1178 :     any => "any",
1179 :     visible => "visible",
1180 :     selected => "selected",
1181 :     none => "no",
1182 :     },
1183 :     -onchange => $onChange,
1184 :     }
1185 : sh002i 1594 ),
1186 : ghe3 6827 CGI::br(),
1187 :     # "existing users and adding",
1188 :     # CGI::popup_menu(
1189 :     # -name => "action.import.add",
1190 :     # -values => [qw(any none)],
1191 :     # -default => $actionParams{"action.import.add"}->[0] || "any",
1192 :     # -labels => {
1193 :     # any => "any",
1194 :     # none => "no",
1195 :     # },
1196 :     # -onchange => $onChange,
1197 :     # ),
1198 :     # "new users",
1199 :     WeBWorK::CGI_labeled_input(
1200 :     -type=>"select",
1201 :     -id=>"import_select_add",
1202 :     -label_text=>"Add which new users?: ",
1203 :     -input_attr=>{
1204 :     -name => "action.import.add",
1205 :     -values => [qw(any none)],
1206 :     -default => $actionParams{"action.import.add"}->[0] || "any",
1207 :     -labels => {
1208 :     any => "any",
1209 :     none => "no",
1210 :     },
1211 :     -onchange => $onChange,
1212 :     }
1213 : sh002i 1594 ),
1214 :     );
1215 :     }
1216 :    
1217 :     sub import_handler {
1218 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
1219 :    
1220 :     my $source = $actionParams->{"action.import.source"}->[0];
1221 :     my $add = $actionParams->{"action.import.add"}->[0];
1222 :     my $replace = $actionParams->{"action.import.replace"}->[0];
1223 :    
1224 :     my $fileName = $source;
1225 :     my $createNew = $add eq "any";
1226 :     my $replaceExisting;
1227 :     my @replaceList;
1228 :     if ($replace eq "any") {
1229 :     $replaceExisting = "any";
1230 :     } elsif ($replace eq "none") {
1231 :     $replaceExisting = "none";
1232 :     } elsif ($replace eq "visible") {
1233 :     $replaceExisting = "listed";
1234 :     @replaceList = @{ $self->{visibleUserIDs} };
1235 :     } elsif ($replace eq "selected") {
1236 :     $replaceExisting = "listed";
1237 :     @replaceList = @{ $self->{selectedUserIDs} };
1238 :     }
1239 :    
1240 :     my ($replaced, $added, $skipped)
1241 :     = $self->importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList);
1242 :    
1243 : sh002i 1597 # make new users visible... do we really want to do this? probably.
1244 :     push @{ $self->{visibleUserIDs} }, @$added;
1245 :    
1246 : sh002i 1594 my $numReplaced = @$replaced;
1247 :     my $numAdded = @$added;
1248 :     my $numSkipped = @$skipped;
1249 :    
1250 :     return $numReplaced . " user" . ($numReplaced == 1 ? "" : "s") . " replaced, "
1251 :     . $numAdded . " user" . ($numAdded == 1 ? "" : "s") . " added, "
1252 : toenail 3060 . $numSkipped . " user" . ($numSkipped == 1 ? "" : "s") . " skipped"
1253 :     . " (" . join (", ", @$skipped) . ") ";
1254 : sh002i 1594 }
1255 :    
1256 :     sub export_form {
1257 :     my ($self, $onChange, %actionParams) = @_;
1258 : sh002i 1597 return join("",
1259 : ghe3 6827 # "Export ",
1260 :     # CGI::popup_menu(
1261 :     # -name => "action.export.scope",
1262 :     # -values => [qw(all visible selected)],
1263 :     # -default => $actionParams{"action.export.scope"}->[0] || "visible",
1264 :     # -labels => {
1265 :     # all => "all users",
1266 :     # visible => "visible users",
1267 :     # selected => "selected users"
1268 :     # },
1269 :     # -onchange => $onChange,
1270 :     # ),
1271 :     WeBWorK::CGI_labeled_input(
1272 :     -type=>"select",
1273 :     -id=>"export_select_scope",
1274 :     -label_text=>"Export which users?: ",
1275 :     -input_attr=>{
1276 :     -name => "action.export.scope",
1277 :     -values => [qw(all visible selected)],
1278 :     -default => $actionParams{"action.export.scope"}->[0] || "visible",
1279 :     -labels => {
1280 :     all => "all users",
1281 :     visible => "visible users",
1282 :     selected => "selected users"
1283 :     },
1284 :     -onchange => $onChange,
1285 :     }
1286 : sh002i 1594 ),
1287 : ghe3 6827 CGI::br(),
1288 :     # " to ",
1289 :     # CGI::popup_menu(
1290 :     # -name=>"action.export.target",
1291 :     # -values => [ "new", $self->getCSVList() ],
1292 :     # -labels => { new => "a new file named:" },
1293 :     # -default => $actionParams{"action.export.target"}->[0] || "",
1294 :     # -onchange => $onChange,
1295 :     # ),
1296 :     WeBWorK::CGI_labeled_input(
1297 :     -type=>"select",
1298 :     -id=>"export_select_target",
1299 :     -label_text=>"Export to what kind of file?: ",
1300 :     -input_attr=>{
1301 :     -name=>"action.export.target",
1302 :     -values => [ "new", $self->getCSVList() ],
1303 :     -labels => { new => "a new file named:" },
1304 :     -default => $actionParams{"action.export.target"}->[0] || "",
1305 :     -onchange => $onChange,
1306 :     }
1307 : sh002i 1597 ),
1308 : ghe3 6827 CGI::br(),
1309 : sh002i 1597 #CGI::br(),
1310 :     #"new file to create: ",
1311 : ghe3 6827 # CGI::textfield(
1312 :     # -name => "action.export.new",
1313 :     # -value => $actionParams{"action.export.new"}->[0] || "",,
1314 :     # -width => "50",
1315 :     # -onchange => $onChange,
1316 :     # ),
1317 :     WeBWorK::CGI_labeled_input(
1318 :     -type=>"text",
1319 :     -id=>"export_filename",
1320 :     -label_text=>"Filename: ",
1321 :     -input_attr=>{
1322 :     -name => "action.export.new",
1323 :     -value => $actionParams{"action.export.new"}->[0] || "",,
1324 :     -width => "50",
1325 :     -onchange => $onChange,
1326 :     }
1327 : sh002i 1594 ),
1328 : sh002i 1597 CGI::tt(".lst"),
1329 : sh002i 1594 );
1330 :     }
1331 :    
1332 : sh002i 1597 sub export_handler {
1333 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
1334 : apizer 3303 my $r = $self->r;
1335 :     my $ce = $r->ce;
1336 :     my $dir = $ce->{courseDirs}->{templates};
1337 : sh002i 1597
1338 :     my $scope = $actionParams->{"action.export.scope"}->[0];
1339 :     my $target = $actionParams->{"action.export.target"}->[0];
1340 :     my $new = $actionParams->{"action.export.new"}->[0];
1341 :    
1342 : apizer 3303 #get name of templates directory as it appears in file manager
1343 :     $dir =~ s|.*/||;
1344 :    
1345 : sh002i 1597 my $fileName;
1346 :     if ($target eq "new") {
1347 :     $fileName = $new;
1348 :     } else {
1349 :     $fileName = $target;
1350 :     }
1351 :    
1352 :     $fileName .= ".lst" unless $fileName =~ m/\.lst$/;
1353 :    
1354 :     my @userIDsToExport;
1355 :     if ($scope eq "all") {
1356 :     @userIDsToExport = @{ $self->{allUserIDs} };
1357 :     } elsif ($scope eq "visible") {
1358 :     @userIDsToExport = @{ $self->{visibleUserIDs} };
1359 :     } elsif ($scope eq "selected") {
1360 :     @userIDsToExport = @{ $self->{selectedUserIDs} };
1361 :     }
1362 :    
1363 :     $self->exportUsersToCSV($fileName, @userIDsToExport);
1364 :    
1365 : apizer 3303 return scalar @userIDsToExport . " users exported to file &nbsp;&nbsp; $dir/$fileName";
1366 : sh002i 1597 }
1367 :    
1368 : sh002i 1594 sub cancelEdit_form {
1369 :     my ($self, $onChange, %actionParams) = @_;
1370 : ghe3 6827 return CGI::span("-Abandon changes");
1371 : sh002i 1594 }
1372 :    
1373 :     sub cancelEdit_handler {
1374 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
1375 : gage 1928 my $r = $self->r;
1376 : sh002i 1594
1377 :     #$self->{selectedUserIDs} = $self->{visibleUserIDs};
1378 :     # only do the above if we arrived here via "edit selected users"
1379 :     if (defined $r->param("prev_visible_users")) {
1380 :     $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
1381 :     } elsif (defined $r->param("no_prev_visible_users")) {
1382 :     $self->{visibleUserIDs} = [];
1383 :     } else {
1384 :     # leave it alone
1385 :     }
1386 :     $self->{editMode} = 0;
1387 :    
1388 :     return "changes abandoned";
1389 :     }
1390 :    
1391 :     sub saveEdit_form {
1392 :     my ($self, $onChange, %actionParams) = @_;
1393 : ghe3 6827 return CGI::span("-Save changes");
1394 : sh002i 1594 }
1395 :    
1396 :     sub saveEdit_handler {
1397 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
1398 : gage 1928 my $r = $self->r;
1399 :     my $db = $r->db;
1400 : sh002i 1594
1401 :     my @visibleUserIDs = @{ $self->{visibleUserIDs} };
1402 :     foreach my $userID (@visibleUserIDs) {
1403 : gage 1667 my $User = $db->getUser($userID); # checked
1404 :     die "record for visible user $userID not found" unless $User;
1405 :     my $PermissionLevel = $db->getPermissionLevel($userID); # checked
1406 :     die "permissions for $userID not defined" unless defined $PermissionLevel;
1407 : sh002i 1594 foreach my $field ($User->NONKEYFIELDS()) {
1408 :     my $param = "user.${userID}.${field}";
1409 :     if (defined $tableParams->{$param}->[0]) {
1410 :     $User->$field($tableParams->{$param}->[0]);
1411 : malsyned 1233 }
1412 :     }
1413 : sh002i 1594
1414 :     foreach my $field ($PermissionLevel->NONKEYFIELDS()) {
1415 :     my $param = "permission.${userID}.${field}";
1416 :     if (defined $tableParams->{$param}->[0]) {
1417 :     $PermissionLevel->$field($tableParams->{$param}->[0]);
1418 :     }
1419 :     }
1420 :    
1421 :     $db->putUser($User);
1422 :     $db->putPermissionLevel($PermissionLevel);
1423 :     }
1424 :    
1425 :     if (defined $r->param("prev_visible_users")) {
1426 :     $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
1427 :     } elsif (defined $r->param("no_prev_visible_users")) {
1428 :     $self->{visibleUserIDs} = [];
1429 :     } else {
1430 :     # leave it alone
1431 :     }
1432 :    
1433 :     $self->{editMode} = 0;
1434 :    
1435 :     return "changes saved";
1436 :     }
1437 :    
1438 : apizer 3287 sub cancelPassword_form {
1439 :     my ($self, $onChange, %actionParams) = @_;
1440 : ghe3 6827 return CGI::span("-Abandon changes");
1441 : apizer 3287 }
1442 :    
1443 :     sub cancelPassword_handler {
1444 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
1445 :     my $r = $self->r;
1446 :    
1447 :     #$self->{selectedUserIDs} = $self->{visibleUserIDs};
1448 :     # only do the above if we arrived here via "edit selected users"
1449 :     if (defined $r->param("prev_visible_users")) {
1450 :     $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
1451 :     } elsif (defined $r->param("no_prev_visible_users")) {
1452 :     $self->{visibleUserIDs} = [];
1453 :     } else {
1454 :     # leave it alone
1455 :     }
1456 :     $self->{passwordMode} = 0;
1457 :    
1458 :     return "changes abandoned";
1459 :     }
1460 :    
1461 :     sub savePassword_form {
1462 :     my ($self, $onChange, %actionParams) = @_;
1463 : ghe3 6827 return CGI::span("-Save changes");
1464 : apizer 3287 }
1465 :    
1466 :     sub savePassword_handler {
1467 :     my ($self, $genericParams, $actionParams, $tableParams) = @_;
1468 :     my $r = $self->r;
1469 :     my $db = $r->db;
1470 :    
1471 :     my @visibleUserIDs = @{ $self->{visibleUserIDs} };
1472 :     foreach my $userID (@visibleUserIDs) {
1473 :     my $User = $db->getUser($userID); # checked
1474 :     die "record for visible user $userID not found" unless $User;
1475 :     my $param = "user.${userID}.new_password";
1476 :     if ((defined $tableParams->{$param}->[0]) and ($tableParams->{$param}->[0])) {
1477 :     my $newP = $tableParams->{$param}->[0];
1478 :     my $Password = eval {$db->getPassword($User->user_id)}; # checked
1479 :     my $cryptPassword = cryptPassword($newP);
1480 :     $Password->password(cryptPassword($newP));
1481 :     eval { $db->putPassword($Password) };
1482 :     }
1483 :     }
1484 :    
1485 :     if (defined $r->param("prev_visible_users")) {
1486 :     $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ];
1487 :     } elsif (defined $r->param("no_prev_visible_users")) {
1488 :     $self->{visibleUserIDs} = [];
1489 :     } else {
1490 :     # leave it alone
1491 :     }
1492 :    
1493 :     $self->{passwordMode} = 0;
1494 :    
1495 :     return "new passwords saved";
1496 :     }
1497 :    
1498 :    
1499 : sh002i 1594 ################################################################################
1500 :     # sorts
1501 :     ################################################################################
1502 :    
1503 : sh002i 2845 sub byUserID { lc $a->user_id cmp lc $b->user_id }
1504 : toenail 3060 sub byFirstName { (defined $a->first_name && defined $b->first_name) ? lc $a->first_name cmp lc $b->first_name : 0; }
1505 :     sub byLastName { (defined $a->last_name && defined $b->last_name ) ? lc $a->last_name cmp lc $b->last_name : 0; }
1506 : sh002i 2845 sub byEmailAddress { lc $a->email_address cmp lc $b->email_address }
1507 :     sub byStudentID { lc $a->student_id cmp lc $b->student_id }
1508 :     sub byStatus { lc $a->status cmp lc $b->status }
1509 :     sub bySection { lc $a->section cmp lc $b->section }
1510 :     sub byRecitation { lc $a->recitation cmp lc $b->recitation }
1511 :     sub byComment { lc $a->comment cmp lc $b->comment }
1512 : apizer 3299 sub byPermission { $a->{permission} <=> $b->{permission} } ## permission level is added to user record hash so we can sort it if necessary
1513 : sh002i 1594
1514 : apizer 3287 # sub byLnFnUid { &byLastName || &byFirstName || &byUserID }
1515 : sh002i 1594
1516 :     ################################################################################
1517 :     # utilities
1518 :     ################################################################################
1519 :    
1520 :     # generate labels for section/recitation popup menus
1521 :     sub menuLabels {
1522 :     my ($self, $hashRef) = @_;
1523 :     my %hash = %$hashRef;
1524 :    
1525 :     my %result;
1526 :     foreach my $key (keys %hash) {
1527 :     my $count = @{ $hash{$key} };
1528 :     my $displayKey = $key || "<none>";
1529 :     $result{$key} = "$displayKey ($count users)";
1530 :     }
1531 :     return %result;
1532 :     }
1533 :    
1534 : sh002i 4722 # FIXME REFACTOR this belongs in a utility class so that addcourse can use it!
1535 :     # (we need a whole suite of higher-level import/export functions somewhere)
1536 : sh002i 1594 sub importUsersFromCSV {
1537 :     my ($self, $fileName, $createNew, $replaceExisting, @replaceList) = @_;
1538 : gage 1928 my $r = $self->r;
1539 :     my $ce = $r->ce;
1540 :     my $db = $r->db;
1541 :     my $dir = $ce->{courseDirs}->{templates};
1542 : toenail 2355 my $user = $r->param('user');
1543 : sh002i 1594
1544 : sh002i 2884 die "illegal character in input: '/'" if $fileName =~ m|/|;
1545 : sh002i 1594 die "won't be able to read from file $dir/$fileName: does it exist? is it readable?"
1546 :     unless -r "$dir/$fileName";
1547 :    
1548 :     my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} };
1549 :     my %replaceOK;
1550 :     if ($replaceExisting eq "none") {
1551 :     %replaceOK = ();
1552 :     } elsif ($replaceExisting eq "listed") {
1553 :     %replaceOK = map { $_ => 1 } @replaceList;
1554 :     } elsif ($replaceExisting eq "any") {
1555 :     %replaceOK = %allUserIDs;
1556 :     }
1557 :    
1558 : sh002i 3708 my $default_permission_level = $ce->{default_permission_level};
1559 :    
1560 : sh002i 1594 my (@replaced, @added, @skipped);
1561 :    
1562 : sh002i 2884 # get list of hashrefs representing lines in classlist file
1563 :     my @classlist = parse_classlist("$dir/$fileName");
1564 :    
1565 : gage 4416 # Default status is enrolled -- fetch abbreviation for enrolled
1566 :     my $default_status_abbrev = $ce->{statuses}->{Enrolled}->{abbrevs}->[0];
1567 :    
1568 : sh002i 2884 foreach my $record (@classlist) {
1569 :     my %record = %$record;
1570 :     my $user_id = $record{user_id};
1571 : sh002i 1594
1572 : gage 6239 unless (WeBWorK::DB::check_user_id($user_id) ) { # try to catch lines with bad characters
1573 :     push @skipped, $user_id;
1574 :     next;
1575 :     }
1576 : toenail 2355 if ($user_id eq $user) { # don't replace yourself!!
1577 :     push @skipped, $user_id;
1578 :     next;
1579 :     }
1580 :    
1581 : sh002i 1594 if (exists $allUserIDs{$user_id} and not exists $replaceOK{$user_id}) {
1582 :     push @skipped, $user_id;
1583 :     next;
1584 :     }
1585 :    
1586 :     if (not exists $allUserIDs{$user_id} and not $createNew) {
1587 :     push @skipped, $user_id;
1588 :     next;
1589 :     }
1590 :    
1591 : sh002i 4722 # set default status is status field is "empty"
1592 :     $record{status} = $default_status_abbrev
1593 :     unless defined $record{status} and $record{status} ne "";
1594 : sh002i 1594
1595 : sh002i 4722 # set password from student ID if password field is "empty"
1596 :     if (not defined $record{password} or $record{password} eq "") {
1597 :     if (defined $record{student_id} and $record{student_id} ne "") {
1598 :     # crypt the student ID and use that
1599 :     $record{password} = cryptPassword($record{student_id});
1600 :     } else {
1601 :     # an empty password field in the database disables password login
1602 :     $record{password} = "";
1603 : sh002i 3897 }
1604 : sh002i 2890 }
1605 :    
1606 : sh002i 4722 # set default permission level if permission level is "empty"
1607 :     $record{permission} = $default_permission_level
1608 : sh002i 4756 unless defined $record{permission} and $record{permission} ne "";
1609 : sh002i 2890
1610 : sh002i 4722 my $User = $db->newUser(%record);
1611 :     my $PermissionLevel = $db->newPermissionLevel(user_id => $user_id, permission => $record{permission});
1612 :     my $Password = $db->newPassword(user_id => $user_id, password => $record{password});
1613 :    
1614 : sh002i 4518 # DBFIXME use REPLACE
1615 : sh002i 1594 if (exists $allUserIDs{$user_id}) {
1616 :     $db->putUser($User);
1617 :     $db->putPermissionLevel($PermissionLevel);
1618 :     $db->putPassword($Password);
1619 :     push @replaced, $user_id;
1620 :     } else {
1621 :     $db->addUser($User);
1622 :     $db->addPermissionLevel($PermissionLevel);
1623 :     $db->addPassword($Password);
1624 :     push @added, $user_id;
1625 :     }
1626 :     }
1627 :    
1628 :     return \@replaced, \@added, \@skipped;
1629 :     }
1630 :    
1631 : sh002i 1597 sub exportUsersToCSV {
1632 :     my ($self, $fileName, @userIDsToExport) = @_;
1633 : gage 1928 my $r = $self->r;
1634 :     my $ce = $r->ce;
1635 :     my $db = $r->db;
1636 :     my $dir = $ce->{courseDirs}->{templates};
1637 : sh002i 1597
1638 : sh002i 2884 die "illegal character in input: '/'" if $fileName =~ m|/|;
1639 : sh002i 1597
1640 : sh002i 2890 my @records;
1641 : sh002i 1597
1642 : sh002i 4518 # DBFIXME use an iterator here
1643 : sh002i 2890 my @Users = $db->getUsers(@userIDsToExport);
1644 :     my @Passwords = $db->getPasswords(@userIDsToExport);
1645 :     my @PermissionLevels = $db->getPermissionLevels(@userIDsToExport);
1646 :     foreach my $i (0 .. $#userIDsToExport) {
1647 :     my $User = $Users[$i];
1648 :     my $Password = $Passwords[$i];
1649 :     my $PermissionLevel = $PermissionLevels[$i];
1650 :     next unless defined $User;
1651 :     my %record = (
1652 :     defined $PermissionLevel ? $PermissionLevel->toHash : (),
1653 :     defined $Password ? $Password->toHash : (),
1654 :     $User->toHash,
1655 :     );
1656 :     push @records, \%record;
1657 :     }
1658 :    
1659 : sh002i 2884 write_classlist("$dir/$fileName", @records);
1660 : sh002i 1597 }
1661 :    
1662 : sh002i 1594 ################################################################################
1663 :     # "display" methods
1664 :     ################################################################################
1665 :    
1666 :     sub fieldEditHTML {
1667 :     my ($self, $fieldName, $value, $properties) = @_;
1668 : sh002i 3690 my $ce = $self->r->ce;
1669 : sh002i 1594 my $size = $properties->{size};
1670 :     my $type = $properties->{type};
1671 :     my $access = $properties->{access};
1672 :     my $items = $properties->{items};
1673 :     my $synonyms = $properties->{synonyms};
1674 :    
1675 : apizer 3660 if ($type eq "email") {
1676 :     if ($value eq '&nbsp;') {
1677 :     return $value;}
1678 :     else {
1679 :     return CGI::a({-href=>"mailto:$value"},$value);
1680 :     }
1681 :     }
1682 :    
1683 : sh002i 1594 if ($access eq "readonly") {
1684 : sh002i 3690 # hack for status
1685 :     if ($type eq "status") {
1686 :     my $status_name = $ce->status_abbrev_to_name($value);
1687 :     if (defined $status_name) {
1688 :     $value = "$status_name ($value)";
1689 :     }
1690 :     }
1691 : sh002i 1594 return $value;
1692 :     }
1693 :    
1694 :     if ($type eq "number" or $type eq "text") {
1695 :     return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size});
1696 :     }
1697 : apizer 3660
1698 : sh002i 1594 if ($type eq "enumerable") {
1699 :     my $matched = undef; # Whether a synonym match has occurred
1700 :    
1701 :     # Process synonyms for enumerable objects
1702 :     foreach my $synonym (keys %$synonyms) {
1703 :     if ($synonym ne "*" and $value =~ m/$synonym/) {
1704 :     $value = $synonyms->{$synonym};
1705 :     $matched = 1;
1706 :     }
1707 :     }
1708 :    
1709 :     if (!$matched and exists $synonyms->{"*"}) {
1710 :     $value = $synonyms->{"*"};
1711 :     }
1712 :    
1713 :     return CGI::popup_menu({
1714 :     name => $fieldName,
1715 :     values => [keys %$items],
1716 :     default => $value,
1717 :     labels => $items,
1718 :     });
1719 :     }
1720 : sh002i 3690
1721 :     if ($type eq "status") {
1722 :     # we used to surreptitously map synonyms to a canonical value...
1723 :     # so should we continue to do that?
1724 :     my $status_name = $ce->status_abbrev_to_name($value);
1725 :     if (defined $status_name) {
1726 :     $value = ($ce->status_name_to_abbrevs($status_name))[0];
1727 :     }
1728 :    
1729 :     my (@values, %labels);
1730 :     while (my ($k, $v) = each %{$ce->{statuses}}) {
1731 :     my @abbrevs = @{$v->{abbrevs}};
1732 :     push @values, $abbrevs[0];
1733 :     foreach my $abbrev (@abbrevs) {
1734 :     $labels{$abbrev} = $k;
1735 :     }
1736 :     }
1737 :    
1738 :     return CGI::popup_menu({
1739 :     name => $fieldName,
1740 :     values => \@values,
1741 :     default => $value,
1742 :     labels => \%labels,
1743 :     });
1744 :     }
1745 : glarose 4913
1746 :     if ($type eq "permission") {
1747 :     my ($default, @values, %labels);
1748 :     my %roles = %{$ce->{userRoles}};
1749 :     foreach my $role (sort {$roles{$a}<=>$roles{$b}} keys(%roles) ) {
1750 :     my $val = $roles{$role};
1751 :    
1752 :     push(@values, $val);
1753 :     $labels{$val} = $role;
1754 :     $default = $val if ( $value eq $role );
1755 :     }
1756 :     return CGI::popup_menu({
1757 : gage 6186 -name => $fieldName,
1758 :     -values => \@values,
1759 : gage 6775 -default => [$default], # force default of 0 to be a selector value (instead of
1760 : gage 6186 # being considered as a null -- now works with CGI 3.42
1761 : gage 6456 #-default => $default, # works with CGI 3.49 (but the above does not, go figure
1762 : gage 6186 -labels => \%labels,
1763 :     -override => 1, # force default value to be selected. (corrects bug on newer CGI
1764 : glarose 4913 });
1765 :     }
1766 : sh002i 1594 }
1767 :    
1768 :     sub recordEditHTML {
1769 :     my ($self, $User, $PermissionLevel, %options) = @_;
1770 : gage 1928 my $r = $self->r;
1771 :     my $urlpath = $r->urlpath;
1772 : toenail 2355 my $db = $r->db;
1773 : gage 1928 my $ce = $r->ce;
1774 : toenail 2355 my $authz = $r->authz;
1775 :     my $user = $r->param('user');
1776 : gage 1928 my $root = $ce->{webworkURLs}->{root};
1777 :     my $courseName = $urlpath->arg("courseID");
1778 : sh002i 1594
1779 :     my $editMode = $options{editMode};
1780 : apizer 3287 my $passwordMode = $options{passwordMode};
1781 : sh002i 1594 my $userSelected = $options{userSelected};
1782 : toenail 2109
1783 : sh002i 3688 my $statusClass = $ce->status_abbrev_to_name($User->status);
1784 : toenail 2355
1785 :     my $sets = $db->countUserSets($User->user_id);
1786 :     my $totalSets = $self->{totalSets};
1787 : sh002i 1594
1788 : gage 1938 my $changeEUserURL = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}),
1789 :     params => {effectiveUser => $User->user_id}
1790 :     );
1791 : sh002i 1594
1792 : gage 3856 my $setsAssignedToUserURL = $self->systemLink($urlpath->new(type=>'instructor_user_detail',
1793 : gage 1938 args=>{courseID => $courseName,
1794 :     userID => $User->user_id
1795 :     }),
1796 :     params => {effectiveUser => $User->user_id}
1797 :     );
1798 : toenail 2285
1799 : apizer 3312 my $userListURL = $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName} )) . "&editMode=1&visible_users=" . $User->user_id;
1800 : toenail 2285
1801 :     my $imageURL = $ce->{webworkURLs}->{htdocs}."/images/edit.gif";
1802 : ghe3 6827 my $imageLink = CGI::a({href => $userListURL}, CGI::img({src=>$imageURL, border=>0, alt=>"Link to Edit Page for ".$User->user_id}));
1803 : sh002i 1640
1804 : sh002i 1594 my @tableCells;
1805 :    
1806 :     # Select
1807 : apizer 3287 if ($editMode or $passwordMode) {
1808 : sh002i 1594 # column not there
1809 :     } else {
1810 :     # selection checkbox
1811 : ghe3 6827 # push @tableCells, CGI::checkbox(
1812 :     # -name => "selected_users",
1813 :     # -value => $User->user_id,
1814 :     # -checked => $userSelected,
1815 :     # -label => "",
1816 :     my $label = "";
1817 : toenail 2355 if ( FIELD_PERMS()->{act_as} and not $authz->hasPermissions($user, FIELD_PERMS()->{act_as}) ){
1818 : ghe3 6827 $label = $User->user_id . $imageLink;
1819 : toenail 2355 } else {
1820 : ghe3 6827 $label = CGI::a({href=>$changeEUserURL}, $User->user_id) . $imageLink;
1821 : toenail 2355 }
1822 : ghe3 6827
1823 :     push @tableCells, WeBWorK::CGI_labeled_input(
1824 :     -type=>"checkbox",
1825 :     -id=>$User->user_id."_checkbox",
1826 :     -label_text=>$label,
1827 :     -input_attr=> $userSelected ?
1828 :     {
1829 :     -name => "selected_users",
1830 :     -value => $User->user_id,
1831 :     -checked => "checked",
1832 :     -class=>"table_checkbox",
1833 :     }
1834 :     :
1835 :     {
1836 :     -name => "selected_users",
1837 :     -value => $User->user_id,
1838 :     -class=>"table_checkbox",
1839 :     }
1840 :     );
1841 : sh002i 1640 }
1842 :    
1843 : ghe3 6827 # Act As
1844 :     # if ($editMode or $passwordMode) {
1845 :     # # column not there
1846 :     # } else {
1847 :     # # selection checkbox
1848 :     # if ( FIELD_PERMS()->{act_as} and not $authz->hasPermissions($user, FIELD_PERMS()->{act_as}) ){
1849 :     # push @tableCells, $User->user_id . $imageLink;
1850 :     # } else {
1851 :     # push @tableCells, CGI::a({href=>$changeEUserURL}, $User->user_id) . $imageLink;
1852 :     # }
1853 :     # }
1854 :    
1855 : toenail 2355 # Login Status
1856 : apizer 3287 if ($editMode or $passwordMode) {
1857 : toenail 2355 # column not there
1858 :     } else {
1859 :     # check to see if a user is currently logged in
1860 : sh002i 4518 # DBFIXME use a WHERE clause
1861 : toenail 2355 my $Key = $db->getKey($User->user_id);
1862 : sh002i 4096 my $is_active = ($Key and time <= $Key->timestamp()+$ce->{sessionKeyTimeout}); # cribbed from check_session
1863 :     push @tableCells, $is_active ? CGI::b("active") : CGI::em("inactive");
1864 : toenail 2355 }
1865 :    
1866 : apizer 3287 # change password (only in password mode)
1867 :     if ($passwordMode) {
1868 :     if ($User->user_id eq $user) {
1869 : ghe3 6827 push @tableCells, CGI::div({-class=>"ResultsWithError"},"You may not change your own password here!") # don't allow a professor to change their own password from this form
1870 : apizer 3287 }
1871 :     else {
1872 :     my $fieldName = 'user.' . $User->user_id . '.' . 'new_password';
1873 : ghe3 6827 push @tableCells, WeBWorK::CGI_labeled_input(-type=>"text", -id=>"password_edit", -label_text=>"New Password: ", -input_attr=>{name=>$fieldName, size=>14});
1874 : apizer 3287 }
1875 :     }
1876 : jj 2961 # User ID (edit mode) or Assigned Sets (otherwise)
1877 : gage 3856 if ( $passwordMode) {
1878 : sh002i 1594 # straight user ID
1879 : toenail 2109 push @tableCells, CGI::div({class=>$statusClass}, $User->user_id);
1880 : gage 3856 } elsif ($editMode) {
1881 :     # straight user ID
1882 :     my $userDetailPage = $urlpath->new(type =>'instructor_user_detail',
1883 :     args =>{
1884 :     courseID => $courseName,
1885 :     userID => $User->user_id, #FIXME eventually this should be a list??
1886 :     }
1887 :     );
1888 :     my $userDetailUrl = $self->systemLink($userDetailPage,params =>{});
1889 :     push @tableCells, CGI::a({href=>$userDetailUrl}, $User->user_id);
1890 :    
1891 : sh002i 1594 } else {
1892 : sh002i 1640 # "edit sets assigned to user" link
1893 : toenail 2355 #push @tableCells, CGI::a({href=>$setsAssignedToUserURL}, "Edit sets");
1894 :     if ( FIELD_PERMS()->{sets} and not $authz->hasPermissions($user, FIELD_PERMS()->{sets}) ) {
1895 :     push @tableCells, "$sets/$totalSets";
1896 :     } else {
1897 :     push @tableCells, CGI::a({href=>$setsAssignedToUserURL}, "$sets/$totalSets");
1898 :     }
1899 : sh002i 1594 }
1900 : toenail 2109
1901 : sh002i 1594 # User Fields
1902 :     foreach my $field ($User->NONKEYFIELDS) {
1903 : apizer 3660 my $fieldName = 'user.' . $User->user_id . '.' . $field,
1904 : sh002i 1594 my $fieldValue = $User->$field;
1905 :     my %properties = %{ FIELD_PROPERTIES()->{$field} };
1906 : apizer 3660 $properties{access} = 'readonly' unless $editMode;
1907 :     $properties{type} = 'email' if ($field eq 'email_address' and !$editMode and !$passwordMode);
1908 : sh002i 1973 $fieldValue = $self->nbsp($fieldValue) unless $editMode;
1909 : toenail 2109 push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties));
1910 : sh002i 1594 }
1911 :    
1912 :     # PermissionLevel Fields
1913 :     foreach my $field ($PermissionLevel->NONKEYFIELDS) {
1914 : apizer 3660 my $fieldName = 'permission.' . $PermissionLevel->user_id . '.' . $field,
1915 : sh002i 1594 my $fieldValue = $PermissionLevel->$field;
1916 : glarose 4913 # get name out of permission level
1917 :     if ( $field eq 'permission' ) {
1918 :     ($fieldValue) = grep { $ce->{userRoles}->{$_} eq $fieldValue } ( keys ( %{$ce->{userRoles}} ) );
1919 :     }
1920 : sh002i 1594 my %properties = %{ FIELD_PROPERTIES()->{$field} };
1921 : apizer 3660 $properties{access} = 'readonly' unless $editMode;
1922 : sh002i 1973 $fieldValue = $self->nbsp($fieldValue) unless $editMode;
1923 : toenail 2109 push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties));
1924 : sh002i 1594 }
1925 :    
1926 : toenail 2285 return CGI::Tr({}, CGI::td({nowrap=>1}, \@tableCells));
1927 : sh002i 1594 }
1928 :    
1929 :     sub printTableHTML {
1930 :     my ($self, $UsersRef, $PermissionLevelsRef, $fieldNamesRef, %options) = @_;
1931 : gage 1928 my $r = $self->r;
1932 : apizer 3302 my $urlpath = $r->urlpath;
1933 :     my $courseName = $urlpath->arg("courseID");
1934 : gage 1928 my $userTemplate = $self->{userTemplate};
1935 : sh002i 1594 my $permissionLevelTemplate = $self->{permissionLevelTemplate};
1936 : gage 1928 my @Users = @$UsersRef;
1937 :     my @PermissionLevels = @$PermissionLevelsRef;
1938 :     my %fieldNames = %$fieldNamesRef;
1939 : sh002i 1594
1940 : gage 1928 my $editMode = $options{editMode};
1941 : apizer 3287 my $passwordMode = $options{passwordMode};
1942 : gage 1928 my %selectedUserIDs = map { $_ => 1 } @{ $options{selectedUserIDs} };
1943 : apizer 3299 # my $currentSort = $options{currentSort};
1944 : apizer 3302 my $primarySortField = $options{primarySortField};
1945 :     my $secondarySortField = $options{secondarySortField};
1946 :     my @visableUserIDs = @{ $options{visableUserIDs} };
1947 :    
1948 : sh002i 1594 # names of headings:
1949 :     my @realFieldNames = (
1950 :     $userTemplate->KEYFIELDS,
1951 :     $userTemplate->NONKEYFIELDS,
1952 :     $permissionLevelTemplate->NONKEYFIELDS,
1953 : malsyned 1233 );
1954 :    
1955 : apizer 3299 # my %sortSubs = %{ SORT_SUBS() };
1956 : sh002i 1594 #my @stateParams = @{ STATE_PARAMS() };
1957 :     #my $hrefPrefix = $r->uri . "?" . $self->url_args(@stateParams); # $self->url_authen_args
1958 :     my @tableHeadings;
1959 :     foreach my $field (@realFieldNames) {
1960 : gage 1938 my $result = $fieldNames{$field};
1961 : sh002i 1594 push @tableHeadings, $result;
1962 :     };
1963 :    
1964 :     # prepend selection checkbox? only if we're NOT editing!
1965 : apizer 3287 unless($editMode or $passwordMode) {
1966 : apizer 3302
1967 : apizer 3312 #warn "line 1582 visibleUserIDs=@visableUserIDs \n";
1968 : apizer 3302 my %current_state =();
1969 : apizer 3304 if (@visableUserIDs) {
1970 :     # This is a hack to get around: Maximum URL Length Is 2,083 Characters in Internet Explorer.
1971 : apizer 3312 # Without passing visable users the URL is about 250 characters. If the total URL is under the limit
1972 : apizer 3304 # we will pass visable users. If it is over, we will not pass any and all users will be displayed.
1973 : apizer 3312 # Maybe we should replace the GET method by POST (but this doesn't look good) --- AKP
1974 : apizer 3304
1975 : apizer 3312 my $visableUserIDsString = join ':', @visableUserIDs;
1976 :     if (length($visableUserIDsString) < 1830) {
1977 : apizer 3304 %current_state = (
1978 :     primarySortField => "$primarySortField",
1979 :     secondarySortField => "$secondarySortField",
1980 : apizer 3312 visable_user_string => "$visableUserIDsString"
1981 : apizer 3304 );
1982 :     } else {
1983 :     %current_state = (
1984 :     primarySortField => "$primarySortField",
1985 :     secondarySortField => "$secondarySortField",
1986 :     show_all_users => "1"
1987 :     );
1988 :     }
1989 :     } else {
1990 : apizer 3302 %current_state = (
1991 :     primarySortField => "$primarySortField",
1992 :     secondarySortField => "$secondarySortField",
1993 :     no_visible_users => "1"
1994 : apizer 3304 );
1995 :     }
1996 : apizer 3302 @tableHeadings = (
1997 : ghe3 6827 #"Select",
1998 : apizer 3302 CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'user_id', %current_state})}, 'Login Name'),
1999 :     "Login Status",
2000 :     "Assigned Sets",
2001 :     CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'first_name', %current_state})}, 'First Name'),
2002 :     CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'last_name', %current_state})}, 'Last Name'),
2003 :     CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'email_address', %current_state})}, 'Email Address'),
2004 :     CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'student_id', %current_state})}, 'Student ID'),
2005 :     CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'status', %current_state})}, 'Status'),
2006 :     CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'section', %current_state})}, 'Section'),
2007 :     CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'recitation', %current_state})}, 'Recitation'),
2008 :     CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'comment', %current_state})}, 'Comment'),
2009 :     CGI::a({href => $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName,} ), params=>{labelSortMethod=>'permission', %current_state})}, 'Permission Level'),
2010 :     )
2011 :     }
2012 : apizer 3287 if($passwordMode) {
2013 :     unshift @tableHeadings, "New Password";
2014 :     }
2015 :    
2016 : sh002i 1594 # print the table
2017 : apizer 3287 if ($editMode or $passwordMode) {
2018 : ghe3 6827 print CGI::start_table({-nowrap=>0, -id=>"classlist_table"});
2019 : sh002i 1594 } else {
2020 : ghe3 6827 print CGI::start_table({-border=>1, -nowrap=>1, -id=>"classlist_table"});
2021 : sh002i 1594 }
2022 :    
2023 :     print CGI::Tr({}, CGI::th({}, \@tableHeadings));
2024 :    
2025 : gage 1713
2026 : sh002i 1594 for (my $i = 0; $i < @Users; $i++) {
2027 :     my $User = $Users[$i];
2028 :     my $PermissionLevel = $PermissionLevels[$i];
2029 :    
2030 :     print $self->recordEditHTML($User, $PermissionLevel,
2031 :     editMode => $editMode,
2032 : apizer 3287 passwordMode => $passwordMode,
2033 : sh002i 1594 userSelected => exists $selectedUserIDs{$User->user_id}
2034 :     );
2035 :     }
2036 :    
2037 :     print CGI::end_table();
2038 : gage 1713 #########################################
2039 :     # if there are no users shown print message
2040 :     #
2041 :     ##########################################
2042 :    
2043 :     print CGI::p(
2044 :     CGI::i("No students shown. Choose one of the options above to
2045 :     list the students in the course.")
2046 :     ) unless @Users;
2047 : sh002i 1594 }
2048 :    
2049 : ghe3 6827 # split_cap subroutine - ghe3
2050 :    
2051 :     # A sort of wrapper for the built-in split function which uses capital letters as a delimiter, and returns a string containing the separated substrings separated by a whitespace. Used to make actionID's more readable.
2052 :    
2053 :     sub split_cap
2054 :     {
2055 :     my $str = shift;
2056 :    
2057 :     my @str_arr = split(//,$str);
2058 :     my $count = scalar(@str_arr);
2059 :    
2060 :     my $i = 0;
2061 :     my $prev = 0;
2062 :     my @result = ();
2063 :     my $hasCapital = 0;
2064 :     foreach(@str_arr){
2065 :     if($_ =~ /[A-Z]/){
2066 :     $hasCapital = 1;
2067 :     push(@result, join("", @str_arr[$prev..$i-1]));
2068 :     $prev = $i;
2069 :     }
2070 :     $i++;
2071 :     }
2072 :    
2073 :     unless($hasCapital){
2074 :     return $str;
2075 :     }
2076 :     else{
2077 :     push(@result, join("", @str_arr[$prev..$count-1]));
2078 :     return join(" ",@result);
2079 :     }
2080 :     }
2081 :    
2082 : sh002i 1594 1;
2083 :    

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9