| 1 | ################################################################################ |
1 | ################################################################################ |
| 2 | # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project |
2 | # WeBWorK Online Homework Delivery System |
| 3 | # $Id$ |
3 | # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ |
|
|
4 | # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm,v 1.48 2004/05/11 20:13:22 toenail Exp $ |
|
|
5 | # |
|
|
6 | # This program is free software; you can redistribute it and/or modify it under |
|
|
7 | # the terms of either: (a) the GNU General Public License as published by the |
|
|
8 | # Free Software Foundation; either version 2, or (at your option) any later |
|
|
9 | # version, or (b) the "Artistic License" which comes with this package. |
|
|
10 | # |
|
|
11 | # This program is distributed in the hope that it will be useful, but WITHOUT |
|
|
12 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
|
13 | # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the |
|
|
14 | # Artistic License for more details. |
| 4 | ################################################################################ |
15 | ################################################################################ |
| 5 | |
16 | |
| 6 | package WeBWorK::ContentGenerator::Instructor::UserList; |
17 | package WeBWorK::ContentGenerator::Instructor::UserList; |
| 7 | use base qw(WeBWorK::ContentGenerator::Instructor); |
18 | use base qw(WeBWorK::ContentGenerator::Instructor); |
| 8 | |
19 | |
| 9 | =head1 NAME |
20 | =head1 NAME |
| 10 | |
21 | |
| 11 | WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific |
22 | WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific |
| 12 | data editing |
23 | data editing |
|
|
24 | |
|
|
25 | =cut |
|
|
26 | |
|
|
27 | =for comment |
|
|
28 | |
|
|
29 | What do we want to be able to do here? |
|
|
30 | |
|
|
31 | Filter what users are shown: |
|
|
32 | - none, all, selected |
|
|
33 | - matching user_id, matching section, matching recitation |
|
|
34 | Switch from view mode to edit mode: |
|
|
35 | - showing visible users |
|
|
36 | - showing selected users |
|
|
37 | Switch from edit mode to view and save changes |
|
|
38 | Switch from edit mode to view and abandon changes |
|
|
39 | Delete users: |
|
|
40 | - visible |
|
|
41 | - selected |
|
|
42 | Import users: |
|
|
43 | - replace: |
|
|
44 | - any users |
|
|
45 | - visible users |
|
|
46 | - selected users |
|
|
47 | - no users |
|
|
48 | - add: |
|
|
49 | - any users |
|
|
50 | - no users |
|
|
51 | Export users: |
|
|
52 | - export: |
|
|
53 | - all |
|
|
54 | - visible |
|
|
55 | - selected |
|
|
56 | - to: |
|
|
57 | - existing file on server (overwrite): [ list of files ] |
|
|
58 | - new file on server (create): [ filename ] |
| 13 | |
59 | |
| 14 | =cut |
60 | =cut |
| 15 | |
61 | |
| 16 | use strict; |
62 | use strict; |
| 17 | use warnings; |
63 | use warnings; |
| 18 | use CGI qw(); |
64 | use CGI qw(); |
| 19 | |
65 | use WeBWorK::Utils qw(readFile readDirectory); |
|
|
66 | use Apache::Constants qw(:common REDIRECT DONE); #FIXME -- this should be called higher up in the object tree. |
| 20 | use constant HIDE_USERS_THRESHHOLD => 20; |
67 | use constant HIDE_USERS_THRESHHOLD => 20; |
|
|
68 | use constant EDIT_FORMS => [qw(cancelEdit saveEdit)]; |
|
|
69 | use constant VIEW_FORMS => [qw(filter edit import export add delete)]; |
|
|
70 | use constant STATE_PARAMS => [qw(user effectiveUser key visible_users no_visible_users prev_visible_users no_prev_visible_users editMode sortField)]; |
|
|
71 | |
|
|
72 | use constant SORT_SUBS => { |
|
|
73 | user_id => \&byUserID, |
|
|
74 | first_name => \&byFirstName, |
|
|
75 | last_name => \&byLastName, |
|
|
76 | email_address => \&byEmailAddress, |
|
|
77 | student_id => \&byStudentID, |
|
|
78 | status => \&byStatus, |
|
|
79 | section => \&bySection, |
|
|
80 | recitation => \&byRecitation, |
|
|
81 | comment => \&byComment, |
|
|
82 | }; |
|
|
83 | |
|
|
84 | use constant FIELD_PROPERTIES => { |
|
|
85 | user_id => { |
|
|
86 | type => "text", |
|
|
87 | size => 8, |
|
|
88 | access => "readonly", |
|
|
89 | }, |
|
|
90 | first_name => { |
|
|
91 | type => "text", |
|
|
92 | size => 10, |
|
|
93 | access => "readwrite", |
|
|
94 | }, |
|
|
95 | last_name => { |
|
|
96 | type => "text", |
|
|
97 | size => 10, |
|
|
98 | access => "readwrite", |
|
|
99 | }, |
|
|
100 | email_address => { |
|
|
101 | type => "text", |
|
|
102 | size => 20, |
|
|
103 | access => "readwrite", |
|
|
104 | }, |
|
|
105 | student_id => { |
|
|
106 | type => "text", |
|
|
107 | size => 11, |
|
|
108 | access => "readwrite", |
|
|
109 | }, |
|
|
110 | status => { |
|
|
111 | type => "enumerable", |
|
|
112 | size => 4, |
|
|
113 | access => "readwrite", |
|
|
114 | items => { |
|
|
115 | "C" => "Enrolled", |
|
|
116 | "D" => "Drop", |
|
|
117 | "A" => "Audit", |
|
|
118 | }, |
|
|
119 | synonyms => { |
|
|
120 | qr/^[ce]/i => "C", |
|
|
121 | qr/^[dw]/i => "D", |
|
|
122 | qr/^a/i => "A", |
|
|
123 | "*" => "C", |
|
|
124 | } |
|
|
125 | }, |
|
|
126 | section => { |
|
|
127 | type => "text", |
|
|
128 | size => 4, |
|
|
129 | access => "readwrite", |
|
|
130 | }, |
|
|
131 | recitation => { |
|
|
132 | type => "text", |
|
|
133 | size => 4, |
|
|
134 | access => "readwrite", |
|
|
135 | }, |
|
|
136 | comment => { |
|
|
137 | type => "text", |
|
|
138 | size => 20, |
|
|
139 | access => "readwrite", |
|
|
140 | }, |
|
|
141 | permission => { |
|
|
142 | type => "number", |
|
|
143 | size => 2, |
|
|
144 | access => "readwrite", |
|
|
145 | } |
|
|
146 | }; |
|
|
147 | sub pre_header_initialize { |
|
|
148 | my $self = shift; |
|
|
149 | my $r = $self->r; |
|
|
150 | my $urlpath = $r->urlpath; |
|
|
151 | my $ce = $r->ce; |
|
|
152 | my $courseName = $urlpath->arg("courseID"); |
|
|
153 | # Handle redirects, if any. |
|
|
154 | ############################## |
|
|
155 | # Redirect to the addUser page |
|
|
156 | ################################## |
|
|
157 | |
|
|
158 | defined($r->param('action')) && $r->param('action') eq 'add' && do { |
|
|
159 | # fix url and redirect |
|
|
160 | my $root = $ce->{webworkURLs}->{root}; |
|
|
161 | |
|
|
162 | my $numberOfStudents = $r->param('number_of_students'); |
|
|
163 | warn "number of students not defined " unless defined $numberOfStudents; |
|
|
164 | |
|
|
165 | my $uri=$self->systemLink( $urlpath->newFromModule('WeBWorK::ContentGenerator::Instructor::AddUsers',courseID=>$courseName), |
|
|
166 | params=>{ |
|
|
167 | number_of_students=>$numberOfStudents, |
|
|
168 | } |
|
|
169 | ); |
|
|
170 | #FIXME does the display mode need to be defined? |
|
|
171 | #FIXME url_authen_args also includes an effective user, so the new one must come first. |
|
|
172 | # even that might not work with every browser since there are two effective User assignments. |
|
|
173 | $r->header_out(Location => $uri); |
|
|
174 | $self->{noContent} = 1; # forces redirect |
|
|
175 | return; |
|
|
176 | }; |
|
|
177 | } |
|
|
178 | # FIXME -- this should be moved up to instructor or contentgenerator |
|
|
179 | sub header { |
|
|
180 | my $self = shift; |
|
|
181 | return REDIRECT if $self->{noContent}; |
|
|
182 | my $r = $self->r; |
|
|
183 | $r->content_type('text/html'); |
|
|
184 | $r->send_http_header(); |
|
|
185 | return OK; |
|
|
186 | } |
|
|
187 | |
|
|
188 | #FIXME -- this should probably be moved up to instructor or contentgenerator as well |
|
|
189 | #sub nbsp { |
|
|
190 | # my $str = shift; |
|
|
191 | # ($str =~/\S/) ? $str : ' ' ; # returns non-breaking space for empty strings |
|
|
192 | # # tricky cases: $str =0; |
|
|
193 | # # $str is a complex number |
|
|
194 | #} |
|
|
195 | # moved to ContentGenerator.pm |
| 21 | |
196 | |
| 22 | sub initialize { |
197 | sub initialize { |
| 23 | my ($self) = @_; |
198 | my ($self) = @_; |
| 24 | my $r = $self->{r}; |
199 | my $r = $self->r; |
| 25 | my $db = $self->{db}; |
200 | my $db = $r->db; |
| 26 | my $ce = $self->{ce}; |
201 | my $ce = $r->ce; |
| 27 | my $authz = $self->{authz}; |
202 | my $authz = $r->authz; |
| 28 | my $user = $r->param('user'); |
203 | my $user = $r->param('user'); |
| 29 | |
204 | |
| 30 | unless ($authz->hasPermissions($user, "modify_student_data")) { |
205 | unless ($authz->hasPermissions($user, "modify_student_data")) { |
| 31 | $self->{submitError} = "You are not authorized to modify student data"; |
206 | $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify student data"))); |
| 32 | return; |
207 | return; |
| 33 | } |
208 | } |
| 34 | |
209 | |
| 35 | 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'))) { |
|
|
| 56 | foreach my $userID ($r->param('selectUser')) { |
|
|
| 57 | $db->deleteUser($userID); |
|
|
| 58 | } |
|
|
| 59 | } elsif (defined($r->param('addStudent'))) { |
210 | #if (defined($r->param('addStudent'))) { |
| 60 | my $newUser = $db->newUser; |
211 | # my $newUser = $db->newUser; |
| 61 | my $newPermissionLevel = $db->newPermissionLevel; |
212 | # my $newPermissionLevel = $db->newPermissionLevel; |
| 62 | my $newPassword = $db->newPassword; |
213 | # my $newPassword = $db->newPassword; |
| 63 | $newUser->user_id($r->param('newUserID')); |
214 | # $newUser->user_id($r->param('newUserID')); |
| 64 | $newPermissionLevel->user_id($r->param('newUserID')); |
215 | # $newPermissionLevel->user_id($r->param('newUserID')); |
| 65 | $newPassword->user_id($r->param('newUserID')); |
216 | # $newPassword->user_id($r->param('newUserID')); |
| 66 | $newUser->status('C'); |
217 | # $newUser->status('C'); |
| 67 | $newPermissionLevel->permission(0); |
218 | # $newPermissionLevel->permission(0); |
| 68 | $db->addUser($newUser); |
219 | # $db->addUser($newUser); |
| 69 | $db->addPermissionLevel($newPermissionLevel); |
220 | # $db->addPermissionLevel($newPermissionLevel); |
| 70 | $db->addPassword($newPassword); |
221 | # $db->addPassword($newPassword); |
| 71 | } |
222 | #} |
| 72 | } |
223 | } |
| 73 | |
224 | |
| 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 | |
225 | |
| 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 | |
226 | |
| 114 | # generate labels for section/recitation popup menus |
227 | sub body { |
| 115 | sub menuLabels { |
228 | my ($self) = @_; |
| 116 | my ($self, $hashRef) = @_; |
229 | my $r = $self->r; |
| 117 | my %hash = %$hashRef; |
230 | my $urlpath = $r->urlpath; |
|
|
231 | my $db = $r->db; |
|
|
232 | my $ce = $r->ce; |
|
|
233 | my $authz = $r->authz; |
|
|
234 | my $courseName = $urlpath->arg("courseID"); |
|
|
235 | my $setID = $urlpath->arg("setID"); |
|
|
236 | my $user = $r->param('user'); |
| 118 | |
237 | |
| 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 { |
|
|
| 129 | my $self = shift; |
|
|
| 130 | return "User List"; |
|
|
| 131 | } |
|
|
| 132 | |
|
|
| 133 | sub path { |
|
|
| 134 | my $self = shift; |
|
|
| 135 | my $args = $_[-1]; |
|
|
| 136 | my $ce = $self->{ce}; |
|
|
| 137 | my $root = $ce->{webworkURLs}->{root}; |
238 | my $root = $ce->{webworkURLs}->{root}; |
| 138 | my $courseName = $ce->{courseName}; |
|
|
| 139 | |
|
|
| 140 | return $self->pathMacro($args, |
|
|
| 141 | "Home" => "$root", |
|
|
| 142 | $courseName => "$root/$courseName", |
|
|
| 143 | "Instructor Tools" => "$root/$courseName/instructor", |
|
|
| 144 | "User List" => "", |
|
|
| 145 | ); |
|
|
| 146 | } |
|
|
| 147 | |
239 | |
| 148 | sub body { |
240 | # templates for getting field names |
| 149 | my ($self, $setID) = @_; |
241 | my $userTemplate = $self->{userTemplate} = $db->newUser; |
| 150 | my $r = $self->{r}; |
242 | my $permissionLevelTemplate = $self->{permissionLevelTemplate} = $db->newPermissionLevel; |
| 151 | my $authz = $self->{authz}; |
|
|
| 152 | my $user = $r->param('user'); |
|
|
| 153 | my $db = $self->{db}; |
|
|
| 154 | my $ce = $self->{ce}; |
|
|
| 155 | my $root = $ce->{webworkURLs}->{root}; |
|
|
| 156 | my $courseName = $ce->{courseName}; |
|
|
| 157 | |
243 | |
| 158 | return CGI::em("You are not authorized to access the Instructor tools.") |
244 | return CGI::em("You are not authorized to access the Instructor tools.") |
| 159 | unless $authz->hasPermissions($user, "access_instructor_tools"); |
245 | unless $authz->hasPermissions($user, "access_instructor_tools"); |
| 160 | |
246 | |
| 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. |
247 | # This table can be consulted when display-ready forms of field names are needed. |
| 170 | my %prettyFieldNames = map { $_ => $_ } ( |
248 | my %prettyFieldNames = map { $_ => $_ } |
| 171 | $userTemplate->FIELDS(), $permissionLevelTemplate->FIELDS()); |
249 | $userTemplate->FIELDS(), |
|
|
250 | $permissionLevelTemplate->FIELDS(); |
| 172 | |
251 | |
| 173 | @prettyFieldNames{qw( |
252 | @prettyFieldNames{qw( |
| 174 | user_id |
253 | user_id |
| 175 | first_name |
254 | first_name |
| 176 | last_name |
255 | last_name |
| … | |
… | |
| 180 | section |
259 | section |
| 181 | recitation |
260 | recitation |
| 182 | comment |
261 | comment |
| 183 | permission |
262 | permission |
| 184 | )} = ( |
263 | )} = ( |
| 185 | "User ID", |
264 | "Assigned sets", |
| 186 | "First Name", |
265 | "First Name", |
| 187 | "Last Name", |
266 | "Last Name", |
| 188 | "E-mail", |
267 | "E-mail", |
| 189 | "Student ID", |
268 | "Student ID", |
| 190 | "Status", |
269 | "Status", |
| … | |
… | |
| 192 | "Recitation", |
271 | "Recitation", |
| 193 | "Comment", |
272 | "Comment", |
| 194 | "Perm. Level" |
273 | "Perm. Level" |
| 195 | ); |
274 | ); |
| 196 | |
275 | |
| 197 | my %fieldProperties = ( |
276 | ########## set initial values for state fields |
| 198 | user_id => { |
277 | |
| 199 | type => "text", |
278 | my @allUserIDs = $db->listUsers; |
| 200 | size => 8, |
279 | $self->{allUserIDs} = \@allUserIDs; |
| 201 | access => "readonly", |
280 | |
|
|
281 | if (defined $r->param("visible_users")) { |
|
|
282 | $self->{visibleUserIDs} = [ $r->param("visible_users") ]; |
|
|
283 | } elsif (defined $r->param("no_visible_users")) { |
|
|
284 | $self->{visibleUserIDs} = []; |
|
|
285 | } else { |
|
|
286 | if (@allUserIDs > HIDE_USERS_THRESHHOLD) { |
|
|
287 | $self->{visibleUserIDs} = []; |
|
|
288 | } else { |
|
|
289 | $self->{visibleUserIDs} = [ @allUserIDs ]; |
| 202 | }, |
290 | } |
| 203 | first_name => { |
291 | } |
| 204 | type => "text", |
292 | |
| 205 | size => 10, |
293 | $self->{prevVisibleUserIDs} = $self->{visibleUserIDs}; |
| 206 | access => $editMode ? "readwrite" : "readonly", |
294 | |
|
|
295 | if (defined $r->param("selected_users")) { |
|
|
296 | $self->{selectedUserIDs} = [ $r->param("selected_users") ]; |
|
|
297 | } else { |
|
|
298 | $self->{selectedUserIDs} = []; |
|
|
299 | } |
|
|
300 | |
|
|
301 | $self->{editMode} = $r->param("editMode") || 0; |
|
|
302 | |
|
|
303 | $self->{sortField} = $r->param("sortField") || "last_name"; |
|
|
304 | |
|
|
305 | my @allUsers = $db->getUsers(@allUserIDs); |
|
|
306 | my (%sections, %recitations); |
|
|
307 | foreach my $User (@allUsers) { |
|
|
308 | push @{$sections{defined $User->section ? $User->section : ""}}, $User->user_id; |
|
|
309 | push @{$recitations{defined $User->recitation ? $User->recitation : ""}}, $User->user_id; |
|
|
310 | } |
|
|
311 | $self->{sections} = \%sections; |
|
|
312 | $self->{recitations} = \%recitations; |
|
|
313 | |
|
|
314 | ########## call action handler |
|
|
315 | |
|
|
316 | my $actionID = $r->param("action"); |
|
|
317 | if ($actionID) { |
|
|
318 | unless (grep { $_ eq $actionID } @{ VIEW_FORMS() }, @{ EDIT_FORMS() }) { |
|
|
319 | die "Action $actionID not found"; |
| 207 | }, |
320 | } |
| 208 | last_name => { |
321 | my $actionHandler = "${actionID}_handler"; |
| 209 | type => "text", |
322 | my %genericParams; |
| 210 | size => 10, |
323 | foreach my $param (qw(selected_users)) { |
| 211 | access => $editMode ? "readwrite" : "readonly", |
324 | $genericParams{$param} = [ $r->param($param) ]; |
| 212 | }, |
325 | } |
| 213 | email_address => { |
326 | my %actionParams = $self->getActionParams($actionID); |
| 214 | type => "text", |
327 | my %tableParams = $self->getTableParams(); |
| 215 | size => 20, |
328 | print CGI::p( |
| 216 | access => $editMode ? "readwrite" : "readonly", |
329 | '<div style="color:green">', |
|
|
330 | "Result of last action performed: ", |
|
|
331 | CGI::i($self->$actionHandler(\%genericParams, \%actionParams, \%tableParams)), |
|
|
332 | '</div>', |
|
|
333 | CGI::hr() |
|
|
334 | |
|
|
335 | ); |
|
|
336 | } |
|
|
337 | |
|
|
338 | ########## retrieve possibly changed values for member fields |
|
|
339 | |
|
|
340 | #@allUserIDs = @{ $self->{allUserIDs} }; # do we need this one? |
|
|
341 | my @visibleUserIDs = @{ $self->{visibleUserIDs} }; |
|
|
342 | my @prevVisibleUserIDs = @{ $self->{prevVisibleUserIDs} }; |
|
|
343 | my @selectedUserIDs = @{ $self->{selectedUserIDs} }; |
|
|
344 | my $editMode = $self->{editMode}; |
|
|
345 | my $sortField = $self->{sortField}; |
|
|
346 | |
|
|
347 | #warn "visibleUserIDs=@visibleUserIDs\n"; |
|
|
348 | #warn "prevVisibleUserIDs=@prevVisibleUserIDs\n"; |
|
|
349 | #warn "selectedUserIDs=@selectedUserIDs\n"; |
|
|
350 | #warn "editMode=$editMode\n"; |
|
|
351 | |
|
|
352 | ########## get required users |
|
|
353 | |
|
|
354 | my @Users = grep { defined $_ } @visibleUserIDs ? $db->getUsers(@visibleUserIDs) : (); |
|
|
355 | |
|
|
356 | # presort users |
|
|
357 | my %sortSubs = %{ SORT_SUBS() }; |
|
|
358 | my $sortSub = $sortSubs{$sortField}; |
|
|
359 | #@Users = sort $sortSub @Users; |
|
|
360 | @Users = sort byLnFnUid @Users; |
|
|
361 | |
|
|
362 | my @PermissionLevels; |
|
|
363 | |
|
|
364 | for (my $i = 0; $i < @Users; $i++) { |
|
|
365 | my $User = $Users[$i]; |
|
|
366 | my $PermissionLevel = $db->getPermissionLevel($User->user_id); # checked |
|
|
367 | |
|
|
368 | unless ($PermissionLevel) { |
|
|
369 | # uh oh! no permission level record found! |
|
|
370 | warn "added missing permission level for user ", $User->user_id, "\n"; |
|
|
371 | |
|
|
372 | # create a new permission level record |
|
|
373 | $PermissionLevel = $db->newPermissionLevel; |
|
|
374 | $PermissionLevel->user_id($User->user_id); |
|
|
375 | $PermissionLevel->permission(0); |
|
|
376 | |
|
|
377 | # add it to the database |
|
|
378 | $db->addPermissionLevel($PermissionLevel); |
| 217 | }, |
379 | } |
| 218 | student_id => { |
380 | |
| 219 | type => "text", |
381 | $PermissionLevels[$i] = $PermissionLevel; |
| 220 | size => 11, |
382 | } |
| 221 | access => $editMode ? "readwrite" : "readonly", |
383 | |
|
|
384 | ########## print beginning of form |
|
|
385 | |
|
|
386 | print CGI::start_form({method=>"post", action=>$self->systemLink($urlpath,authen=>0), name=>"userlist"}); |
|
|
387 | print $self->hidden_authen_fields(); |
|
|
388 | |
|
|
389 | ########## print state data |
|
|
390 | |
|
|
391 | print "\n<!-- state data here -->\n"; |
|
|
392 | |
|
|
393 | if (@visibleUserIDs) { |
|
|
394 | print CGI::hidden(-name=>"visible_users", -value=>\@visibleUserIDs); |
|
|
395 | } else { |
|
|
396 | print CGI::hidden(-name=>"no_visible_users", -value=>"1"); |
|
|
397 | } |
|
|
398 | |
|
|
399 | if (@prevVisibleUserIDs) { |
|
|
400 | print CGI::hidden(-name=>"prev_visible_users", -value=>\@prevVisibleUserIDs); |
|
|
401 | } else { |
|
|
402 | print CGI::hidden(-name=>"no_prev_visible_users", -value=>"1"); |
|
|
403 | } |
|
|
404 | |
|
|
405 | print CGI::hidden(-name=>"editMode", -value=>$editMode); |
|
|
406 | |
|
|
407 | print CGI::hidden(-name=>"sortField", -value=>$sortField); |
|
|
408 | |
|
|
409 | print "\n<!-- state data here -->\n"; |
|
|
410 | |
|
|
411 | ########## print action forms |
|
|
412 | |
|
|
413 | print CGI::start_table({}); |
|
|
414 | print CGI::Tr({}, CGI::td({-colspan=>2}, "Select an action to perform:")); |
|
|
415 | |
|
|
416 | my @formsToShow; |
|
|
417 | if ($editMode) { |
|
|
418 | @formsToShow = @{ EDIT_FORMS() }; |
|
|
419 | } else { |
|
|
420 | @formsToShow = @{ VIEW_FORMS() }; |
|
|
421 | } |
|
|
422 | |
|
|
423 | my $i = 0; |
|
|
424 | foreach my $actionID (@formsToShow) { |
|
|
425 | my $actionForm = "${actionID}_form"; |
|
|
426 | my $onChange = "document.userlist.action[$i].checked=true"; |
|
|
427 | my %actionParams = $self->getActionParams($actionID); |
|
|
428 | |
|
|
429 | print CGI::Tr({-valign=>"top"}, |
|
|
430 | CGI::td({}, CGI::input({-type=>"radio", -name=>"action", -value=>$actionID})), |
|
|
431 | CGI::td({}, $self->$actionForm($onChange, %actionParams)) |
|
|
432 | ); |
|
|
433 | |
|
|
434 | $i++; |
|
|
435 | } |
|
|
436 | |
|
|
437 | print CGI::Tr({}, CGI::td({-colspan=>2, -align=>"center"}, |
|
|
438 | CGI::submit(-value=>"Take Action!")) |
|
|
439 | ); |
|
|
440 | print CGI::end_table(); |
|
|
441 | |
|
|
442 | ########## print table |
|
|
443 | |
|
|
444 | print CGI::p("Showing ", scalar @visibleUserIDs, " out of ", scalar @allUserIDs, " users."); |
|
|
445 | |
|
|
446 | $self->printTableHTML(\@Users, \@PermissionLevels, \%prettyFieldNames, |
|
|
447 | editMode => $editMode, |
|
|
448 | selectedUserIDs => \@selectedUserIDs, |
|
|
449 | ); |
|
|
450 | |
|
|
451 | |
|
|
452 | ########## print end of form |
|
|
453 | |
|
|
454 | print CGI::end_form(); |
|
|
455 | |
|
|
456 | return ""; |
|
|
457 | } |
|
|
458 | |
|
|
459 | ################################################################################ |
|
|
460 | # extract particular params and put them in a hash (values are ARRAYREFs!) |
|
|
461 | ################################################################################ |
|
|
462 | |
|
|
463 | sub getActionParams { |
|
|
464 | my ($self, $actionID) = @_; |
|
|
465 | my $r = $self->{r}; |
|
|
466 | |
|
|
467 | my %actionParams; |
|
|
468 | foreach my $param ($r->param) { |
|
|
469 | next unless $param =~ m/^action\.$actionID\./; |
|
|
470 | $actionParams{$param} = [ $r->param($param) ]; |
|
|
471 | } |
|
|
472 | return %actionParams; |
|
|
473 | } |
|
|
474 | |
|
|
475 | sub getTableParams { |
|
|
476 | my ($self) = @_; |
|
|
477 | my $r = $self->{r}; |
|
|
478 | |
|
|
479 | my %tableParams; |
|
|
480 | foreach my $param ($r->param) { |
|
|
481 | next unless $param =~ m/^(?:user|permission)\./; |
|
|
482 | $tableParams{$param} = [ $r->param($param) ]; |
|
|
483 | } |
|
|
484 | return %tableParams; |
|
|
485 | } |
|
|
486 | |
|
|
487 | ################################################################################ |
|
|
488 | # actions and action triggers |
|
|
489 | ################################################################################ |
|
|
490 | |
|
|
491 | # filter, edit, cancelEdit, and saveEdit should stay with the display module and |
|
|
492 | # not be real "actions". that way, all actions are shown in view mode and no |
|
|
493 | # actions are shown in edit mode. |
|
|
494 | |
|
|
495 | sub filter_form { |
|
|
496 | my ($self, $onChange, %actionParams) = @_; |
|
|
497 | #return CGI::table({}, CGI::Tr({-valign=>"top"}, |
|
|
498 | # CGI::td({}, |
|
|
499 | return join("", |
|
|
500 | "Show ", |
|
|
501 | CGI::popup_menu( |
|
|
502 | -name => "action.filter.scope", |
|
|
503 | -values => [qw(all none selected match_ids match_section match_recitation)], |
|
|
504 | -default => $actionParams{"action.filter.scope"}->[0] || "match_ids", |
|
|
505 | -labels => { |
|
|
506 | all => "all users", |
|
|
507 | none => "no users", |
|
|
508 | selected => "users checked below", |
|
|
509 | match_ids => "users with matching user IDs:", |
|
|
510 | match_section => "users in selected section", |
|
|
511 | match_recitation => "users in selected recitation", |
| 222 | }, |
512 | }, |
| 223 | status => { |
513 | -onchange => $onChange, |
| 224 | type => "enumerable", |
514 | ), |
| 225 | size => 4, |
515 | " ", |
| 226 | access => $editMode ? "readwrite" : "readonly", |
516 | CGI::textfield( |
| 227 | items => { |
517 | -name => "action.filter.user_ids", |
| 228 | "C" => "Enrolled", |
518 | -value => $actionParams{"action.filter.user_ids"}->[0] || "",, |
| 229 | "D" => "Drop", |
519 | -width => "50", |
| 230 | "A" => "Audit", |
520 | -onchange => $onChange, |
|
|
521 | ), |
|
|
522 | " (separate multiple IDs with commas)", |
|
|
523 | CGI::br(), |
|
|
524 | "sections: ", |
|
|
525 | CGI::popup_menu( |
|
|
526 | -name => "action.filter.section", |
|
|
527 | -values => [ keys %{ $self->{sections} } ], |
|
|
528 | -default => $actionParams{"action.filter.section"}->[0] || "", |
|
|
529 | -labels => { $self->menuLabels($self->{sections}) }, |
|
|
530 | -onchange => $onChange, |
|
|
531 | ), |
|
|
532 | " recitations: ", |
|
|
533 | CGI::popup_menu( |
|
|
534 | -name => "action.filter.recitation", |
|
|
535 | -values => [ keys %{ $self->{recitations} } ], |
|
|
536 | -default => $actionParams{"action.filter.recitation"}->[0] || "", |
|
|
537 | -labels => { $self->menuLabels($self->{recitations}) }, |
|
|
538 | -onchange => $onChange, |
|
|
539 | ), |
|
|
540 | ); |
|
|
541 | # ), |
|
|
542 | #)); |
|
|
543 | } |
|
|
544 | |
|
|
545 | # this action handler modifies the "visibleUserIDs" field based on the contents |
|
|
546 | # of the "action.filter.scope" parameter and the "selected_users" |
|
|
547 | sub filter_handler { |
|
|
548 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
|
|
549 | |
|
|
550 | my $result; |
|
|
551 | |
|
|
552 | my $scope = $actionParams->{"action.filter.scope"}->[0]; |
|
|
553 | if ($scope eq "all") { |
|
|
554 | $result = "showing all users"; |
|
|
555 | $self->{visibleUserIDs} = $self->{allUserIDs}; |
|
|
556 | } elsif ($scope eq "none") { |
|
|
557 | $result = "showing no users"; |
|
|
558 | $self->{visibleUserIDs} = []; |
|
|
559 | } elsif ($scope eq "selected") { |
|
|
560 | $result = "showing selected users"; |
|
|
561 | $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref |
|
|
562 | } elsif ($scope eq "match_ids") { |
|
|
563 | my @userIDs = split /\s*,\s*/, $actionParams->{"action.filter.user_ids"}->[0]; |
|
|
564 | $self->{visibleUserIDs} = \@userIDs; |
|
|
565 | } elsif ($scope eq "match_section") { |
|
|
566 | my $section = $actionParams->{"action.filter.section"}->[0]; |
|
|
567 | $self->{visibleUserIDs} = $self->{sections}->{$section}; # an arrayref |
|
|
568 | } elsif ($scope eq "match_recitation") { |
|
|
569 | my $recitation = $actionParams->{"action.filter.recitation"}->[0]; |
|
|
570 | $self->{visibleUserIDs} = $self->{recitations}->{$recitation}; # an arrayref |
|
|
571 | } |
|
|
572 | |
|
|
573 | return $result; |
|
|
574 | } |
|
|
575 | |
|
|
576 | sub edit_form { |
|
|
577 | my ($self, $onChange, %actionParams) = @_; |
|
|
578 | return join("", |
|
|
579 | "Edit ", |
|
|
580 | CGI::popup_menu( |
|
|
581 | -name => "action.edit.scope", |
|
|
582 | -values => [qw(all visible selected)], |
|
|
583 | -default => $actionParams{"action.edit.scope"}->[0] || "selected", |
|
|
584 | -labels => { |
|
|
585 | all => "all users", |
|
|
586 | visible => "visible users", |
|
|
587 | selected => "selected users" |
| 231 | }, |
588 | }, |
| 232 | synonyms => { |
589 | -onchange => $onChange, |
| 233 | qr/^[ce]/i => "C", |
|
|
| 234 | qr/^[dw]/i => "D", |
|
|
| 235 | qr/^a/i => "A", |
|
|
| 236 | "*" => "C", |
|
|
| 237 | } |
|
|
| 238 | }, |
|
|
| 239 | section => { |
|
|
| 240 | type => "text", |
|
|
| 241 | size => 4, |
|
|
| 242 | access => $editMode ? "readwrite" : "readonly", |
|
|
| 243 | }, |
|
|
| 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 | } |
|
|
| 259 | ); |
|
|
| 260 | |
|
|
| 261 | my @userIDs = $db->listUsers; |
|
|
| 262 | my @userRecords = $db->getUsers(@userIDs); |
|
|
| 263 | |
|
|
| 264 | my (%sections, %recitations); |
|
|
| 265 | foreach my $user (@userRecords) { |
|
|
| 266 | push @{$sections{$user->section}}, $user; |
|
|
| 267 | push @{$recitations{$user->recitation}}, $user; |
|
|
| 268 | } |
|
|
| 269 | |
|
|
| 270 | my $filter_type = $r->param("filter_type") |
|
|
| 271 | || (@userIDs > HIDE_USERS_THRESHHOLD ? "none" : "all"); |
|
|
| 272 | my $filter_user_id = $filter_type eq "filter_user_id" |
|
|
| 273 | ? $r->param("filter_user_id") || "" |
|
|
| 274 | : ""; |
|
|
| 275 | my $filter_section = $filter_type eq "filter_section" |
|
|
| 276 | ? $r->param("filter_section") || "" |
|
|
| 277 | : ""; |
|
|
| 278 | my $filter_recitation = $filter_type eq "filter_recitation" |
|
|
| 279 | ? $r->param("filter_recitation") || "" |
|
|
| 280 | : ""; |
|
|
| 281 | |
|
|
| 282 | # override filter selection if "Edit Selected Users" button is pressed |
|
|
| 283 | if (defined $r->param("edit_selected")) { |
|
|
| 284 | $filter_type = "filter_selected"; |
|
|
| 285 | } |
|
|
| 286 | |
|
|
| 287 | if ($filter_type eq "none") { |
|
|
| 288 | @userRecords = (); |
|
|
| 289 | } elsif ($filter_type eq "filter_selected") { |
|
|
| 290 | @userRecords = (); |
|
|
| 291 | my @userIDs = $r->param("selectUser"); |
|
|
| 292 | if (@userIDs) { |
|
|
| 293 | @userRecords = $db->getUsers(@userIDs); |
|
|
| 294 | } |
|
|
| 295 | } elsif ($filter_type eq "filter_user_id") { |
|
|
| 296 | @userRecords = (); |
|
|
| 297 | if ($filter_user_id ne "") { |
|
|
| 298 | my $userRecord = $db->getUser($filter_user_id); |
|
|
| 299 | @userRecords = ($userRecord) if $userRecord; |
|
|
| 300 | } |
|
|
| 301 | } elsif ($filter_type eq "filter_section") { |
|
|
| 302 | @userRecords = (); |
|
|
| 303 | @userRecords = @{$sections{$filter_section}} |
|
|
| 304 | if exists $sections{$filter_section}; |
|
|
| 305 | } elsif ($filter_type eq "filter_recitation") { |
|
|
| 306 | @userRecords = (); |
|
|
| 307 | @userRecords = @{$recitations{$filter_recitation}} |
|
|
| 308 | if exists $recitations{$filter_recitation}; |
|
|
| 309 | } |
|
|
| 310 | |
|
|
| 311 | @userRecords = sort { |
|
|
| 312 | (lc $a->section cmp lc $b->section) |
|
|
| 313 | || (lc $a->last_name cmp lc $b->last_name) |
|
|
| 314 | || (lc $a->first_name cmp lc $b->first_name) |
|
|
| 315 | || (lc $a->user_id cmp lc $b->user_id) |
|
|
| 316 | } @userRecords; |
|
|
| 317 | |
|
|
| 318 | print CGI::start_form({method=>"post", action=>$r->uri()}); |
|
|
| 319 | print $self->hidden_authen_fields(); |
|
|
| 320 | |
|
|
| 321 | # filter options |
|
|
| 322 | my %labels = ( |
|
|
| 323 | none => "No users", |
|
|
| 324 | all => "All " . scalar @userIDs . " users", |
|
|
| 325 | filter_selected => "Users selected below", |
|
|
| 326 | filter_user_id => "User with ID " . CGI::input({ |
|
|
| 327 | type=>"text", |
|
|
| 328 | name=>"filter_user_id", |
|
|
| 329 | value=>$filter_user_id, |
|
|
| 330 | size=>"20" |
|
|
| 331 | }), |
|
|
| 332 | filter_section => "Users in section " . CGI::popup_menu( |
|
|
| 333 | -name=>"filter_section", |
|
|
| 334 | -values=>[ keys %sections ], |
|
|
| 335 | -labels=>{ $self->menuLabels(\%sections) }, |
|
|
| 336 | -default=>$filter_section, |
|
|
| 337 | ), |
|
|
| 338 | filter_recitation => "Users in recitation " . CGI::popup_menu( |
|
|
| 339 | -name=>"filter_recitation", |
|
|
| 340 | -values=>[ sort keys %recitations ], |
|
|
| 341 | -labels=>{ $self->menuLabels(\%recitations) }, |
|
|
| 342 | -default=>$filter_recitation, |
|
|
| 343 | ), |
590 | ), |
| 344 | ); |
591 | ); |
|
|
592 | } |
|
|
593 | |
|
|
594 | sub edit_handler { |
|
|
595 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
| 345 | |
596 | |
|
|
597 | my $result; |
|
|
598 | |
|
|
599 | my $scope = $actionParams->{"action.edit.scope"}->[0]; |
|
|
600 | if ($scope eq "all") { |
|
|
601 | $result = "editing all users"; |
|
|
602 | $self->{visibleUserIDs} = $self->{allUserIDs}; |
|
|
603 | } elsif ($scope eq "visible") { |
|
|
604 | $result = "editing visible users"; |
|
|
605 | # leave visibleUserIDs alone |
|
|
606 | } elsif ($scope eq "selected") { |
|
|
607 | $result = "editing selected users"; |
|
|
608 | $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref |
|
|
609 | } |
|
|
610 | $self->{editMode} = 1; |
|
|
611 | |
|
|
612 | return $result; |
|
|
613 | } |
|
|
614 | |
|
|
615 | sub delete_form { |
|
|
616 | my ($self, $onChange, %actionParams) = @_; |
|
|
617 | return join("", |
|
|
618 | qq!\n<div style="background-color:red">!, |
|
|
619 | "Delete ", |
|
|
620 | CGI::popup_menu( |
|
|
621 | -name => "action.delete.scope", |
|
|
622 | -values => [qw(none visible selected)], |
|
|
623 | -default => $actionParams{"action.delete.scope"}->[0] || "none", |
|
|
624 | -labels => { |
|
|
625 | none => "no users.", |
|
|
626 | #visible => "visible users.", |
|
|
627 | selected => "selected users." |
|
|
628 | }, |
|
|
629 | -onchange => $onChange, |
|
|
630 | ), |
|
|
631 | CGI::em(" Deletion destroys all user-related data and is not undoable!"), |
|
|
632 | "</div>\n", |
|
|
633 | ); |
|
|
634 | } |
|
|
635 | |
|
|
636 | sub delete_handler { |
|
|
637 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
|
|
638 | my $r = $self->r; |
|
|
639 | my $db = $r->db; |
|
|
640 | my $scope = $actionParams->{"action.delete.scope"}->[0]; |
|
|
641 | |
|
|
642 | my @userIDsToDelete = (); |
|
|
643 | #if ($scope eq "visible") { |
|
|
644 | # @userIDsToDelete = @{ $self->{visibleUserIDs} }; |
|
|
645 | #} elsif ($scope eq "selected") { |
|
|
646 | if ($scope eq "selected") { |
|
|
647 | @userIDsToDelete = @{ $self->{selectedUserIDs} }; |
|
|
648 | } |
|
|
649 | |
|
|
650 | my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} }; |
|
|
651 | my %visibleUserIDs = map { $_ => 1 } @{ $self->{visibleUserIDs} }; |
|
|
652 | my %selectedUserIDs = map { $_ => 1 } @{ $self->{selectedUserIDs} }; |
|
|
653 | |
|
|
654 | foreach my $userID (@userIDsToDelete) { |
|
|
655 | delete $allUserIDs{$userID}; |
|
|
656 | delete $visibleUserIDs{$userID}; |
|
|
657 | delete $selectedUserIDs{$userID}; |
|
|
658 | $db->deleteUser($userID); |
|
|
659 | } |
|
|
660 | |
|
|
661 | $self->{allUserIDs} = [ keys %allUserIDs ]; |
|
|
662 | $self->{visibleUserIDs} = [ keys %visibleUserIDs ]; |
|
|
663 | $self->{selectedUserIDs} = [ keys %selectedUserIDs ]; |
|
|
664 | |
|
|
665 | my $num = @userIDsToDelete; |
|
|
666 | return "deleted $num user" . ($num == 1 ? "" : "s"); |
|
|
667 | } |
|
|
668 | sub add_form { |
|
|
669 | my ($self, $onChange, %actionParams) = @_; |
|
|
670 | |
|
|
671 | return "Add ", CGI::input({name=>'number_of_students', value=>1,size => 3}), " student(s). "; |
|
|
672 | } |
|
|
673 | |
|
|
674 | sub add_handler { |
|
|
675 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
|
|
676 | # This action is redirected to the addUser.pm module using ../instructor/add_user/... |
|
|
677 | return "Nothing done by add student handler"; |
|
|
678 | } |
|
|
679 | sub import_form { |
|
|
680 | my ($self, $onChange, %actionParams) = @_; |
|
|
681 | return join(" ", |
|
|
682 | "Import users from file", |
|
|
683 | CGI::popup_menu( |
|
|
684 | -name => "action.import.source", |
|
|
685 | -values => [ "", $self->getCSVList() ], |
|
|
686 | -default => $actionParams{"action.import.source"}->[0] || "", |
|
|
687 | -onchange => $onChange, |
|
|
688 | ), |
|
|
689 | "replacing", |
|
|
690 | CGI::popup_menu( |
|
|
691 | -name => "action.import.replace", |
|
|
692 | -values => [qw(any visible selected none)], |
|
|
693 | -default => $actionParams{"action.import.replace"}->[0] || "none", |
|
|
694 | -labels => { |
|
|
695 | any => "any", |
|
|
696 | visible => "visible", |
|
|
697 | selected => "selected", |
|
|
698 | none => "no", |
|
|
699 | }, |
|
|
700 | -onchange => $onChange, |
|
|
701 | ), |
|
|
702 | "existing users and adding", |
|
|
703 | CGI::popup_menu( |
|
|
704 | -name => "action.import.add", |
|
|
705 | -values => [qw(any none)], |
|
|
706 | -default => $actionParams{"action.import.add"}->[0] || "any", |
|
|
707 | -labels => { |
|
|
708 | any => "any", |
|
|
709 | none => "no", |
|
|
710 | }, |
|
|
711 | -onchange => $onChange, |
|
|
712 | ), |
|
|
713 | "new users", |
|
|
714 | ); |
|
|
715 | } |
|
|
716 | |
|
|
717 | sub import_handler { |
|
|
718 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
|
|
719 | |
|
|
720 | my $source = $actionParams->{"action.import.source"}->[0]; |
|
|
721 | my $add = $actionParams->{"action.import.add"}->[0]; |
|
|
722 | my $replace = $actionParams->{"action.import.replace"}->[0]; |
|
|
723 | |
|
|
724 | my $fileName = $source; |
|
|
725 | my $createNew = $add eq "any"; |
|
|
726 | my $replaceExisting; |
|
|
727 | my @replaceList; |
|
|
728 | if ($replace eq "any") { |
|
|
729 | $replaceExisting = "any"; |
|
|
730 | } elsif ($replace eq "none") { |
|
|
731 | $replaceExisting = "none"; |
|
|
732 | } elsif ($replace eq "visible") { |
|
|
733 | $replaceExisting = "listed"; |
|
|
734 | @replaceList = @{ $self->{visibleUserIDs} }; |
|
|
735 | } elsif ($replace eq "selected") { |
|
|
736 | $replaceExisting = "listed"; |
|
|
737 | @replaceList = @{ $self->{selectedUserIDs} }; |
|
|
738 | } |
|
|
739 | |
|
|
740 | my ($replaced, $added, $skipped) |
|
|
741 | = $self->importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList); |
|
|
742 | |
|
|
743 | # make new users visible... do we really want to do this? probably. |
|
|
744 | push @{ $self->{visibleUserIDs} }, @$added; |
|
|
745 | |
|
|
746 | my $numReplaced = @$replaced; |
|
|
747 | my $numAdded = @$added; |
|
|
748 | my $numSkipped = @$skipped; |
|
|
749 | |
|
|
750 | return $numReplaced . " user" . ($numReplaced == 1 ? "" : "s") . " replaced, " |
|
|
751 | . $numAdded . " user" . ($numAdded == 1 ? "" : "s") . " added, " |
|
|
752 | . $numSkipped . " user" . ($numSkipped == 1 ? "" : "s") . " skipped."; |
|
|
753 | } |
|
|
754 | |
|
|
755 | sub export_form { |
|
|
756 | my ($self, $onChange, %actionParams) = @_; |
|
|
757 | return join("", |
|
|
758 | "Export ", |
|
|
759 | CGI::popup_menu( |
|
|
760 | -name => "action.export.scope", |
|
|
761 | -values => [qw(all visible selected)], |
|
|
762 | -default => $actionParams{"action.export.scope"}->[0] || "visible", |
|
|
763 | -labels => { |
|
|
764 | all => "all users", |
|
|
765 | visible => "visible users", |
|
|
766 | selected => "selected users" |
|
|
767 | }, |
|
|
768 | -onchange => $onChange, |
|
|
769 | ), |
|
|
770 | " to ", |
|
|
771 | CGI::popup_menu( |
|
|
772 | -name=>"action.export.target", |
|
|
773 | -values => [ "new", $self->getCSVList() ], |
|
|
774 | -labels => { new => "a new file named:" }, |
|
|
775 | -default => $actionParams{"action.export.target"}->[0] || "", |
|
|
776 | -onchange => $onChange, |
|
|
777 | ), |
|
|
778 | #CGI::br(), |
|
|
779 | #"new file to create: ", |
|
|
780 | CGI::textfield( |
|
|
781 | -name => "action.export.new", |
|
|
782 | -value => $actionParams{"action.export.new"}->[0] || "",, |
|
|
783 | -width => "50", |
|
|
784 | -onchange => $onChange, |
|
|
785 | ), |
|
|
786 | CGI::tt(".lst"), |
|
|
787 | ); |
|
|
788 | } |
|
|
789 | |
|
|
790 | sub export_handler { |
|
|
791 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
|
|
792 | |
|
|
793 | my $scope = $actionParams->{"action.export.scope"}->[0]; |
|
|
794 | my $target = $actionParams->{"action.export.target"}->[0]; |
|
|
795 | my $new = $actionParams->{"action.export.new"}->[0]; |
|
|
796 | |
|
|
797 | my $fileName; |
|
|
798 | if ($target eq "new") { |
|
|
799 | $fileName = $new; |
|
|
800 | } else { |
|
|
801 | $fileName = $target; |
|
|
802 | } |
|
|
803 | |
|
|
804 | $fileName .= ".lst" unless $fileName =~ m/\.lst$/; |
|
|
805 | |
|
|
806 | my @userIDsToExport; |
|
|
807 | if ($scope eq "all") { |
|
|
808 | @userIDsToExport = @{ $self->{allUserIDs} }; |
|
|
809 | } elsif ($scope eq "visible") { |
|
|
810 | @userIDsToExport = @{ $self->{visibleUserIDs} }; |
|
|
811 | } elsif ($scope eq "selected") { |
|
|
812 | @userIDsToExport = @{ $self->{selectedUserIDs} }; |
|
|
813 | } |
|
|
814 | |
|
|
815 | $self->exportUsersToCSV($fileName, @userIDsToExport); |
|
|
816 | |
|
|
817 | return scalar @userIDsToExport . " users exported"; |
|
|
818 | } |
|
|
819 | |
|
|
820 | sub cancelEdit_form { |
|
|
821 | my ($self, $onChange, %actionParams) = @_; |
|
|
822 | return "Abandon changes"; |
|
|
823 | } |
|
|
824 | |
|
|
825 | sub cancelEdit_handler { |
|
|
826 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
|
|
827 | my $r = $self->r; |
|
|
828 | |
|
|
829 | #$self->{selectedUserIDs} = $self->{visibleUserIDs}; |
|
|
830 | # only do the above if we arrived here via "edit selected users" |
|
|
831 | if (defined $r->param("prev_visible_users")) { |
|
|
832 | $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ]; |
|
|
833 | } elsif (defined $r->param("no_prev_visible_users")) { |
|
|
834 | $self->{visibleUserIDs} = []; |
|
|
835 | } else { |
|
|
836 | # leave it alone |
|
|
837 | } |
|
|
838 | $self->{editMode} = 0; |
|
|
839 | |
|
|
840 | return "changes abandoned"; |
|
|
841 | } |
|
|
842 | |
|
|
843 | sub saveEdit_form { |
|
|
844 | my ($self, $onChange, %actionParams) = @_; |
|
|
845 | return "Save changes"; |
|
|
846 | } |
|
|
847 | |
|
|
848 | sub saveEdit_handler { |
|
|
849 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
|
|
850 | my $r = $self->r; |
|
|
851 | my $db = $r->db; |
|
|
852 | |
|
|
853 | my @visibleUserIDs = @{ $self->{visibleUserIDs} }; |
|
|
854 | foreach my $userID (@visibleUserIDs) { |
|
|
855 | my $User = $db->getUser($userID); # checked |
|
|
856 | die "record for visible user $userID not found" unless $User; |
|
|
857 | my $PermissionLevel = $db->getPermissionLevel($userID); # checked |
|
|
858 | die "permissions for $userID not defined" unless defined $PermissionLevel; |
|
|
859 | foreach my $field ($User->NONKEYFIELDS()) { |
|
|
860 | my $param = "user.${userID}.${field}"; |
|
|
861 | if (defined $tableParams->{$param}->[0]) { |
|
|
862 | $User->$field($tableParams->{$param}->[0]); |
|
|
863 | } |
|
|
864 | } |
|
|
865 | |
|
|
866 | foreach my $field ($PermissionLevel->NONKEYFIELDS()) { |
|
|
867 | my $param = "permission.${userID}.${field}"; |
|
|
868 | if (defined $tableParams->{$param}->[0]) { |
|
|
869 | $PermissionLevel->$field($tableParams->{$param}->[0]); |
|
|
870 | } |
|
|
871 | } |
|
|
872 | |
|
|
873 | $db->putUser($User); |
|
|
874 | $db->putPermissionLevel($PermissionLevel); |
|
|
875 | } |
|
|
876 | |
|
|
877 | if (defined $r->param("prev_visible_users")) { |
|
|
878 | $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ]; |
|
|
879 | } elsif (defined $r->param("no_prev_visible_users")) { |
|
|
880 | $self->{visibleUserIDs} = []; |
|
|
881 | } else { |
|
|
882 | # leave it alone |
|
|
883 | } |
|
|
884 | |
|
|
885 | $self->{editMode} = 0; |
|
|
886 | |
|
|
887 | return "changes saved"; |
|
|
888 | } |
|
|
889 | |
|
|
890 | ################################################################################ |
|
|
891 | # sorts |
|
|
892 | ################################################################################ |
|
|
893 | |
|
|
894 | sub byUserID { $a->user_id cmp $b->user_id } |
|
|
895 | sub byFirstName { $a->first_name cmp $b->first_name } |
|
|
896 | sub byLastName { $a->last_name cmp $b->last_name } |
|
|
897 | sub byEmailAddress { $a->email_address cmp $b->email_address } |
|
|
898 | sub byStudentID { $a->student_id cmp $b->student_id } |
|
|
899 | sub byStatus { $a->status cmp $b->status } |
|
|
900 | sub bySection { $a->section cmp $b->section } |
|
|
901 | sub byRecitation { $a->recitation cmp $b->recitation } |
|
|
902 | sub byComment { $a->comment cmp $b->comment } |
|
|
903 | |
|
|
904 | sub byLnFnUid { &byLastName || &byFirstName || &byUserID } |
|
|
905 | |
|
|
906 | ################################################################################ |
|
|
907 | # utilities |
|
|
908 | ################################################################################ |
|
|
909 | |
|
|
910 | # generate labels for section/recitation popup menus |
|
|
911 | sub menuLabels { |
|
|
912 | my ($self, $hashRef) = @_; |
|
|
913 | my %hash = %$hashRef; |
|
|
914 | |
|
|
915 | my %result; |
|
|
916 | foreach my $key (keys %hash) { |
|
|
917 | my $count = @{ $hash{$key} }; |
|
|
918 | my $displayKey = $key || "<none>"; |
|
|
919 | $result{$key} = "$displayKey ($count users)"; |
|
|
920 | } |
|
|
921 | return %result; |
|
|
922 | } |
|
|
923 | |
|
|
924 | sub importUsersFromCSV { |
|
|
925 | my ($self, $fileName, $createNew, $replaceExisting, @replaceList) = @_; |
|
|
926 | my $r = $self->r; |
|
|
927 | my $ce = $r->ce; |
|
|
928 | my $db = $r->db; |
|
|
929 | my $dir = $ce->{courseDirs}->{templates}; |
|
|
930 | |
|
|
931 | die "illegal character in input: \"/\"" if $fileName =~ m|/|; |
|
|
932 | die "won't be able to read from file $dir/$fileName: does it exist? is it readable?" |
|
|
933 | unless -r "$dir/$fileName"; |
|
|
934 | |
|
|
935 | my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} }; |
|
|
936 | my %replaceOK; |
|
|
937 | if ($replaceExisting eq "none") { |
|
|
938 | %replaceOK = (); |
|
|
939 | } elsif ($replaceExisting eq "listed") { |
|
|
940 | %replaceOK = map { $_ => 1 } @replaceList; |
|
|
941 | } elsif ($replaceExisting eq "any") { |
|
|
942 | %replaceOK = %allUserIDs; |
|
|
943 | } |
|
|
944 | |
|
|
945 | my (@replaced, @added, @skipped); |
|
|
946 | |
|
|
947 | my @contents = split /\n/, readFile("$dir/$fileName"); |
|
|
948 | foreach my $string (@contents) { |
|
|
949 | $string =~ s/^\s+//; |
|
|
950 | $string =~ s/\s+$//; |
|
|
951 | my ( |
|
|
952 | $student_id, $last_name, $first_name, $status, $comment, |
|
|
953 | $section, $recitation, $email_address, $user_id |
|
|
954 | ) = split /\s*,\s*/, $string; |
|
|
955 | |
|
|
956 | if (exists $allUserIDs{$user_id} and not exists $replaceOK{$user_id}) { |
|
|
957 | push @skipped, $user_id; |
|
|
958 | next; |
|
|
959 | } |
|
|
960 | |
|
|
961 | if (not exists $allUserIDs{$user_id} and not $createNew) { |
|
|
962 | push @skipped, $user_id; |
|
|
963 | next; |
|
|
964 | } |
|
|
965 | |
|
|
966 | my $User = $db->newUser; |
|
|
967 | $User->user_id($user_id); |
|
|
968 | $User->first_name($first_name); |
|
|
969 | $User->last_name($last_name); |
|
|
970 | $User->email_address($email_address); |
|
|
971 | $User->student_id($student_id); |
|
|
972 | $User->status($status); |
|
|
973 | $User->section($section); |
|
|
974 | $User->recitation($recitation); |
|
|
975 | $User->comment($comment); |
|
|
976 | |
|
|
977 | my $PermissionLevel = $db->newPermissionLevel; |
|
|
978 | $PermissionLevel->user_id($user_id); |
|
|
979 | $PermissionLevel->permission(0); |
|
|
980 | |
|
|
981 | my $Password = $db->newPassword; |
|
|
982 | $Password->user_id($user_id); |
|
|
983 | $Password->password(cryptPassword($student_id)); |
|
|
984 | |
|
|
985 | if (exists $allUserIDs{$user_id}) { |
|
|
986 | $db->putUser($User); |
|
|
987 | $db->putPermissionLevel($PermissionLevel); |
|
|
988 | $db->putPassword($Password); |
|
|
989 | push @replaced, $user_id; |
|
|
990 | } else { |
|
|
991 | $db->addUser($User); |
|
|
992 | $db->addPermissionLevel($PermissionLevel); |
|
|
993 | $db->addPassword($Password); |
|
|
994 | push @added, $user_id; |
|
|
995 | } |
|
|
996 | } |
|
|
997 | |
|
|
998 | return \@replaced, \@added, \@skipped; |
|
|
999 | } |
|
|
1000 | |
|
|
1001 | sub exportUsersToCSV { |
|
|
1002 | my ($self, $fileName, @userIDsToExport) = @_; |
|
|
1003 | my $r = $self->r; |
|
|
1004 | my $ce = $r->ce; |
|
|
1005 | my $db = $r->db; |
|
|
1006 | my $dir = $ce->{courseDirs}->{templates}; |
|
|
1007 | |
|
|
1008 | die "illegal character in input: \"/\"" if $fileName =~ m|/|; |
|
|
1009 | |
|
|
1010 | open my $fh, ">", "$dir/$fileName" |
|
|
1011 | or die "failed to open file $dir/$fileName for writing: $!\n"; |
|
|
1012 | |
|
|
1013 | foreach my $userID (@userIDsToExport) { |
|
|
1014 | my $User = $db->getUser($userID); # checked |
|
|
1015 | die "record for user $userID not found." unless $User; |
|
|
1016 | my @fields = ( |
|
|
1017 | $User->student_id, |
|
|
1018 | $User->last_name, |
|
|
1019 | $User->first_name, |
|
|
1020 | $User->status, |
|
|
1021 | $User->comment, |
|
|
1022 | $User->section, |
|
|
1023 | $User->recitation, |
|
|
1024 | $User->email_address, |
|
|
1025 | $User->user_id, |
|
|
1026 | ); |
|
|
1027 | my $string = join ",", @fields; |
|
|
1028 | print $fh "$string\n"; |
|
|
1029 | } |
|
|
1030 | |
|
|
1031 | close $fh; |
|
|
1032 | } |
|
|
1033 | |
|
|
1034 | ################################################################################ |
|
|
1035 | # "display" methods |
|
|
1036 | ################################################################################ |
|
|
1037 | |
|
|
1038 | sub fieldEditHTML { |
|
|
1039 | my ($self, $fieldName, $value, $properties) = @_; |
|
|
1040 | my $size = $properties->{size}; |
|
|
1041 | my $type = $properties->{type}; |
|
|
1042 | my $access = $properties->{access}; |
|
|
1043 | my $items = $properties->{items}; |
|
|
1044 | my $synonyms = $properties->{synonyms}; |
|
|
1045 | |
|
|
1046 | if ($access eq "readonly") { |
|
|
1047 | return $value; |
|
|
1048 | } |
|
|
1049 | |
|
|
1050 | if ($type eq "number" or $type eq "text") { |
|
|
1051 | return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size}); |
|
|
1052 | } |
|
|
1053 | |
|
|
1054 | if ($type eq "enumerable") { |
|
|
1055 | my $matched = undef; # Whether a synonym match has occurred |
|
|
1056 | |
|
|
1057 | # Process synonyms for enumerable objects |
|
|
1058 | foreach my $synonym (keys %$synonyms) { |
|
|
1059 | if ($synonym ne "*" and $value =~ m/$synonym/) { |
|
|
1060 | $value = $synonyms->{$synonym}; |
|
|
1061 | $matched = 1; |
|
|
1062 | } |
|
|
1063 | } |
|
|
1064 | |
|
|
1065 | if (!$matched and exists $synonyms->{"*"}) { |
|
|
1066 | $value = $synonyms->{"*"}; |
|
|
1067 | } |
|
|
1068 | |
|
|
1069 | return CGI::popup_menu({ |
|
|
1070 | name => $fieldName, |
|
|
1071 | values => [keys %$items], |
|
|
1072 | default => $value, |
|
|
1073 | labels => $items, |
|
|
1074 | }); |
|
|
1075 | } |
|
|
1076 | } |
|
|
1077 | |
|
|
1078 | sub recordEditHTML { |
|
|
1079 | my ($self, $User, $PermissionLevel, %options) = @_; |
|
|
1080 | my $r = $self->r; |
|
|
1081 | my $urlpath = $r->urlpath; |
|
|
1082 | my $ce = $r->ce; |
|
|
1083 | my $root = $ce->{webworkURLs}->{root}; |
|
|
1084 | my $courseName = $urlpath->arg("courseID"); |
|
|
1085 | |
|
|
1086 | my $editMode = $options{editMode}; |
|
|
1087 | my $userSelected = $options{userSelected}; |
|
|
1088 | |
|
|
1089 | my $statusClass = $ce->{siteDefaults}->{status}->{$User->{status}}; |
|
|
1090 | |
|
|
1091 | my $changeEUserURL = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}), |
|
|
1092 | params => {effectiveUser => $User->user_id} |
|
|
1093 | ); |
|
|
1094 | |
|
|
1095 | my $setsAssignedToUserURL = $self->systemLink($urlpath->new(type=>'instructor_sets_assigned_to_user', |
|
|
1096 | args=>{courseID => $courseName, |
|
|
1097 | userID => $User->user_id |
|
|
1098 | }), |
|
|
1099 | params => {effectiveUser => $User->user_id} |
|
|
1100 | ); |
|
|
1101 | |
|
|
1102 | my @tableCells; |
|
|
1103 | |
|
|
1104 | # Select |
| 346 | if ($editMode) { |
1105 | if ($editMode) { |
| 347 | print CGI::hidden( |
1106 | # column not there |
| 348 | -name=>"filter_type", |
1107 | } else { |
| 349 | -value=>"filter_selected", |
1108 | # selection checkbox |
|
|
1109 | push @tableCells, CGI::checkbox( |
|
|
1110 | -name => "selected_users", |
|
|
1111 | -value => $User->user_id, |
|
|
1112 | -checked => $userSelected, |
|
|
1113 | -label => "", |
| 350 | ); |
1114 | ); |
|
|
1115 | } |
|
|
1116 | |
|
|
1117 | # Act As |
|
|
1118 | if ($editMode) { |
|
|
1119 | # column not there |
| 351 | } else { |
1120 | } else { |
| 352 | my $cgi = new CGI; |
1121 | # selection checkbox |
| 353 | $cgi->autoEscape(0); |
1122 | push @tableCells, CGI::a({href=>$changeEUserURL}, $User->user_id); |
| 354 | print "Show:", CGI::br(); |
1123 | } |
| 355 | print $cgi->radio_group( |
1124 | |
| 356 | -name=>"filter_type", |
1125 | # User ID |
| 357 | -values=>[ qw(none all filter_selected filter_user_id filter_section filter_recitation) ], |
1126 | if ($editMode) { |
| 358 | -default=>$filter_type, |
1127 | # straight user ID |
| 359 | -linebreak=>"true", |
1128 | push @tableCells, CGI::div({class=>$statusClass}, $User->user_id); |
| 360 | -labels=>\%labels, |
1129 | } else { |
| 361 | -rows=>3, |
1130 | # "edit sets assigned to user" link |
| 362 | -columns=>2, |
1131 | push @tableCells, CGI::a({href=>$setsAssignedToUserURL}, "Edit sets"); |
|
|
1132 | } |
|
|
1133 | |
|
|
1134 | # User Fields |
|
|
1135 | foreach my $field ($User->NONKEYFIELDS) { |
|
|
1136 | my $fieldName = "user." . $User->user_id . "." . $field, |
|
|
1137 | my $fieldValue = $User->$field; |
|
|
1138 | my %properties = %{ FIELD_PROPERTIES()->{$field} }; |
|
|
1139 | $properties{access} = "readonly" unless $editMode; |
|
|
1140 | $fieldValue = $self->nbsp($fieldValue) unless $editMode; |
|
|
1141 | push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties)); |
|
|
1142 | } |
|
|
1143 | |
|
|
1144 | # PermissionLevel Fields |
|
|
1145 | foreach my $field ($PermissionLevel->NONKEYFIELDS) { |
|
|
1146 | my $fieldName = "permission." . $PermissionLevel->user_id . "." . $field, |
|
|
1147 | my $fieldValue = $PermissionLevel->$field; |
|
|
1148 | my %properties = %{ FIELD_PROPERTIES()->{$field} }; |
|
|
1149 | $properties{access} = "readonly" unless $editMode; |
|
|
1150 | $fieldValue = $self->nbsp($fieldValue) unless $editMode; |
|
|
1151 | push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties)); |
|
|
1152 | } |
|
|
1153 | |
|
|
1154 | return CGI::Tr({}, CGI::td({}, \@tableCells)); |
|
|
1155 | } |
|
|
1156 | |
|
|
1157 | sub printTableHTML { |
|
|
1158 | my ($self, $UsersRef, $PermissionLevelsRef, $fieldNamesRef, %options) = @_; |
|
|
1159 | my $r = $self->r; |
|
|
1160 | my $userTemplate = $self->{userTemplate}; |
|
|
1161 | my $permissionLevelTemplate = $self->{permissionLevelTemplate}; |
|
|
1162 | my @Users = @$UsersRef; |
|
|
1163 | my @PermissionLevels = @$PermissionLevelsRef; |
|
|
1164 | my %fieldNames = %$fieldNamesRef; |
|
|
1165 | |
|
|
1166 | my $editMode = $options{editMode}; |
|
|
1167 | my %selectedUserIDs = map { $_ => 1 } @{ $options{selectedUserIDs} }; |
|
|
1168 | my $currentSort = $options{currentSort}; |
|
|
1169 | |
|
|
1170 | # names of headings: |
|
|
1171 | my @realFieldNames = ( |
|
|
1172 | $userTemplate->KEYFIELDS, |
|
|
1173 | $userTemplate->NONKEYFIELDS, |
|
|
1174 | $permissionLevelTemplate->NONKEYFIELDS, |
|
|
1175 | ); |
|
|
1176 | |
|
|
1177 | my %sortSubs = %{ SORT_SUBS() }; |
|
|
1178 | #my @stateParams = @{ STATE_PARAMS() }; |
|
|
1179 | #my $hrefPrefix = $r->uri . "?" . $self->url_args(@stateParams); # $self->url_authen_args |
|
|
1180 | my @tableHeadings; |
|
|
1181 | foreach my $field (@realFieldNames) { |
|
|
1182 | my $result = $fieldNames{$field}; |
|
|
1183 | push @tableHeadings, $result; |
|
|
1184 | }; |
|
|
1185 | |
|
|
1186 | # prepend selection checkbox? only if we're NOT editing! |
|
|
1187 | unshift @tableHeadings, "Select", "Act As" unless $editMode; |
|
|
1188 | |
|
|
1189 | # print the table |
|
|
1190 | if ($editMode) { |
|
|
1191 | print CGI::start_table({}); |
|
|
1192 | } else { |
|
|
1193 | print CGI::start_table({-border=>1}); |
|
|
1194 | } |
|
|
1195 | |
|
|
1196 | print CGI::Tr({}, CGI::th({}, \@tableHeadings)); |
|
|
1197 | |
|
|
1198 | |
|
|
1199 | for (my $i = 0; $i < @Users; $i++) { |
|
|
1200 | my $User = $Users[$i]; |
|
|
1201 | my $PermissionLevel = $PermissionLevels[$i]; |
|
|
1202 | |
|
|
1203 | print $self->recordEditHTML($User, $PermissionLevel, |
|
|
1204 | editMode => $editMode, |
|
|
1205 | userSelected => exists $selectedUserIDs{$User->user_id} |
| 363 | ); |
1206 | ); |
| 364 | print CGI::submit({name=>"filter", value=>"Filter"}); |
|
|
| 365 | } |
|
|
| 366 | |
|
|
| 367 | print CGI::start_table({}); |
|
|
| 368 | |
|
|
| 369 | # Table headings, prettied-up |
|
|
| 370 | my @tableHeadings = ( |
|
|
| 371 | ($editMode ? () : "Select"), |
|
|
| 372 | map {$prettyFieldNames{$_}} ( |
|
|
| 373 | $userTemplate->KEYFIELDS(), |
|
|
| 374 | $userTemplate->NONKEYFIELDS(), |
|
|
| 375 | $permissionLevelTemplate->NONKEYFIELDS(), |
|
|
| 376 | ), |
|
|
| 377 | ); |
|
|
| 378 | |
|
|
| 379 | # now print them |
|
|
| 380 | print CGI::Tr({}, |
|
|
| 381 | CGI::th({}, \@tableHeadings) |
|
|
| 382 | ); |
|
|
| 383 | |
|
|
| 384 | my @userIDsForHiddenSelectField; |
|
|
| 385 | |
|
|
| 386 | # process user records |
|
|
| 387 | foreach my $userRecord (@userRecords) { |
|
|
| 388 | my $currentUser = $userRecord->user_id; |
|
|
| 389 | push @userIDsForHiddenSelectField, $currentUser; |
|
|
| 390 | my $permissionLevel = $db->getPermissionLevel($currentUser); |
|
|
| 391 | unless (defined $permissionLevel) { |
|
|
| 392 | warn "No permissionLevel record for user $currentUser -- added"; |
|
|
| 393 | my $newPermissionLevel = $db->newPermissionLevel; |
|
|
| 394 | $newPermissionLevel->user_id($currentUser); |
|
|
| 395 | $newPermissionLevel->permission(0); |
|
|
| 396 | $db->addPermissionLevel($newPermissionLevel); |
|
|
| 397 | $permissionLevel = $newPermissionLevel; |
|
|
| 398 | # permission set to minimum level |
|
|
| 399 | } |
|
|
| 400 | |
|
|
| 401 | # A concise way of printing a row containing a cell for each field, editable unless it's a key |
|
|
| 402 | print CGI::Tr({}, |
|
|
| 403 | CGI::td({}, [ |
|
|
| 404 | ($editMode |
|
|
| 405 | ? () # don't show selection checkbox if we're in edit mode -- hidden field below |
|
|
| 406 | #: CGI::input({type=>"checkbox", name=>"selectUser", value=>$currentUser}) |
|
|
| 407 | : CGI::checkbox( |
|
|
| 408 | -name=>"selectUser", |
|
|
| 409 | -value=>$currentUser, |
|
|
| 410 | -checked=>($filter_type eq "filter_selected" and not defined $r->param("editingAllVisibleUsers")), |
|
|
| 411 | -label=>"" |
|
|
| 412 | ) |
|
|
| 413 | ), |
|
|
| 414 | ($editMode |
|
|
| 415 | ? $currentUser |
|
|
| 416 | : (map { |
|
|
| 417 | my $changeEUserURL = "$root/$courseName?" |
|
|
| 418 | . "user=" . $r->param("user") |
|
|
| 419 | . "&effectiveUser=" . $userRecord->user_id |
|
|
| 420 | . "&key=" . $r->param("key"); |
|
|
| 421 | CGI::a({href=>$changeEUserURL}, $userRecord->$_) |
|
|
| 422 | } $userRecord->KEYFIELDS) |
|
|
| 423 | ), |
|
|
| 424 | (map { |
|
|
| 425 | $self->fieldEditHTML( |
|
|
| 426 | "user." . $userRecord->user_id . "." .$_, |
|
|
| 427 | $userRecord->$_, $fieldProperties{$_}); |
|
|
| 428 | } $userRecord->NONKEYFIELDS()), |
|
|
| 429 | (map { |
|
|
| 430 | $self->fieldEditHTML( |
|
|
| 431 | "permission." . $permissionLevel->user_id . "." . $_, |
|
|
| 432 | $permissionLevel->$_, $fieldProperties{$_}); |
|
|
| 433 | } $permissionLevel->NONKEYFIELDS()), |
|
|
| 434 | ]) |
|
|
| 435 | ); |
|
|
| 436 | } |
|
|
| 437 | |
|
|
| 438 | unless (@userRecords) { |
|
|
| 439 | print CGI::Tr({}, |
|
|
| 440 | CGI::td({-colspan=>scalar(@tableHeadings), -align=>"center"}, |
|
|
| 441 | "No users match the filter criteria above." |
|
|
| 442 | ), |
|
|
| 443 | ); |
|
|
| 444 | } |
1207 | } |
| 445 | |
1208 | |
| 446 | print CGI::end_table(); |
1209 | print CGI::end_table(); |
|
|
1210 | ######################################### |
|
|
1211 | # if there are no users shown print message |
|
|
1212 | # |
|
|
1213 | ########################################## |
| 447 | |
1214 | |
| 448 | if ($editMode) { |
1215 | print CGI::p( |
| 449 | print CGI::hidden(-name=>"selectUser", -value=>[ @userIDsForHiddenSelectField ]); |
1216 | CGI::i("No students shown. Choose one of the options above to |
| 450 | if (defined $r->param("edit_visible")) { |
1217 | list the students in the course.") |
| 451 | print CGI::hidden(-name=>"editingAllVisibleUsers", -value=>1); |
1218 | ) unless @Users; |
| 452 | } |
|
|
| 453 | } |
|
|
| 454 | |
|
|
| 455 | if ($editMode) { |
|
|
| 456 | print CGI::submit({name=>"discard_chagnes", value=>"Discard Changes to Users"}); |
|
|
| 457 | print CGI::submit({name=>"save_classlist", value=>"Save Changes to Users"}); |
|
|
| 458 | } else { |
|
|
| 459 | print CGI::submit({name=>"edit_visible", value=>"Edit Visible Users"}); |
|
|
| 460 | print CGI::submit({name=>"edit_selected", value=>"Edit Selected Users"}); |
|
|
| 461 | print CGI::submit({name=>"delete_selected", value=>"Delete Selected Users"}); |
|
|
| 462 | } |
|
|
| 463 | |
|
|
| 464 | print CGI::end_form(); |
|
|
| 465 | |
|
|
| 466 | # Add a student form |
|
|
| 467 | unless ($editMode) { |
|
|
| 468 | print CGI::start_form({method=>"post", action=>$r->uri()}); |
|
|
| 469 | print $self->hidden_authen_fields(); |
|
|
| 470 | print "User ID:"; |
|
|
| 471 | print CGI::input({type=>"text", name=>"newUserID", value=>"", size=>"20"}); |
|
|
| 472 | print CGI::submit({name=>"addStudent", value=>"Add User"}); |
|
|
| 473 | print CGI::end_form(); |
|
|
| 474 | } |
|
|
| 475 | |
|
|
| 476 | return ""; |
|
|
| 477 | } |
1219 | } |
| 478 | |
1220 | |
| 479 | 1; |
1221 | 1; |
|
|
1222 | |