| … | |
… | |
| 11 | WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific |
11 | WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific |
| 12 | data editing |
12 | data editing |
| 13 | |
13 | |
| 14 | =cut |
14 | =cut |
| 15 | |
15 | |
|
|
16 | =for comment |
|
|
17 | |
|
|
18 | What do we want to be able to do here? |
|
|
19 | |
|
|
20 | Filter what users are shown: |
|
|
21 | - none, all, selected |
|
|
22 | - matching user_id, matching section, matching recitation |
|
|
23 | Switch from view mode to edit mode: |
|
|
24 | - showing visible users |
|
|
25 | - showing selected users |
|
|
26 | Switch from edit mode to view and save changes |
|
|
27 | Switch from edit mode to view and abandon changes |
|
|
28 | Delete users: |
|
|
29 | - visible |
|
|
30 | - selected |
|
|
31 | Import users: |
|
|
32 | - replace: |
|
|
33 | - any users |
|
|
34 | - visible users |
|
|
35 | - selected users |
|
|
36 | - no users |
|
|
37 | - add: |
|
|
38 | - any users |
|
|
39 | - no users |
|
|
40 | Export 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 | |
| 16 | use strict; |
52 | use strict; |
| 17 | use warnings; |
53 | use warnings; |
| 18 | use CGI qw(); |
54 | use CGI qw(); |
|
|
55 | use WeBWorK::Utils qw(readFile readDirectory); |
| 19 | |
56 | |
| 20 | use constant HIDE_USERS_THRESHHOLD => 20; |
57 | use constant HIDE_USERS_THRESHHOLD => 20; |
|
|
58 | use constant EDIT_FORMS => [qw(cancelEdit saveEdit)]; |
|
|
59 | use constant VIEW_FORMS => [qw(filter edit delete import export)]; |
|
|
60 | use constant STATE_PARAMS => [qw(user effectiveUser key visible_users no_visible_users prev_visible_users no_prev_visible_users editMode sortField)]; |
|
|
61 | |
|
|
62 | use 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 | |
|
|
74 | use 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 | |
| 22 | sub initialize { |
138 | sub 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 | |
| 74 | sub 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 |
|
|
| 115 | sub 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 | |
|
|
| 128 | sub title { |
168 | sub 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 | |
|
|
403 | sub 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 | |
|
|
415 | sub 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 | |
|
|
431 | sub 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" |
|
|
451 | sub 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 | |
|
|
471 | sub 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 | |
|
|
489 | sub 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 | |
|
|
510 | sub 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 | |
|
|
531 | sub 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 | |
|
|
564 | sub 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 | |
|
|
603 | sub 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 | |
|
|
638 | sub 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 | |
|
|
682 | sub cancelEdit_form { |
|
|
683 | my ($self, $onChange, %actionParams) = @_; |
|
|
684 | return "Abandon changes"; |
|
|
685 | } |
|
|
686 | |
|
|
687 | sub 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 | |
|
|
705 | sub saveEdit_form { |
|
|
706 | my ($self, $onChange, %actionParams) = @_; |
|
|
707 | return "Save changes"; |
|
|
708 | } |
|
|
709 | |
|
|
710 | sub 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 | |
|
|
755 | sub byUserID { $a->user_id cmp $b->user_id } |
|
|
756 | sub byFirstName { $a->first_name cmp $b->first_name } |
|
|
757 | sub byLastName { $a->last_name cmp $b->last_name } |
|
|
758 | sub byEmailAddress { $a->email_address cmp $b->email_address } |
|
|
759 | sub byStudentID { $a->student_id cmp $b->student_id } |
|
|
760 | sub byStatus { $a->status cmp $b->status } |
|
|
761 | sub bySection { $a->section cmp $b->section } |
|
|
762 | sub byRecitation { $a->recitation cmp $b->recitation } |
|
|
763 | sub byComment { $a->comment cmp $b->comment } |
|
|
764 | |
|
|
765 | sub byLnFnUid { &byLastName || &byFirstName || &byUserID } |
|
|
766 | |
|
|
767 | ################################################################################ |
|
|
768 | # utilities |
|
|
769 | ################################################################################ |
|
|
770 | |
|
|
771 | # generate labels for section/recitation popup menus |
|
|
772 | sub 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 | |
|
|
785 | sub 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 | |
|
|
792 | sub 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 | |
|
|
872 | sub 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 | |
|
|
912 | sub 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 | |
|
|
972 | sub 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 | |
|
|
1031 | 1; |
|
|
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 | |
|
|
| 479 | 1; |
|
|