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

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

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

Revision 1584 Revision 2109
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
6package WeBWorK::ContentGenerator::Instructor::UserList; 17package WeBWorK::ContentGenerator::Instructor::UserList;
7use base qw(WeBWorK::ContentGenerator::Instructor); 18use base qw(WeBWorK::ContentGenerator::Instructor);
8 19
9=head1 NAME 20=head1 NAME
10 21
11WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific 22WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific
12data editing 23data editing
24
25=cut
26
27=for comment
28
29What do we want to be able to do here?
30
31Filter what users are shown:
32 - none, all, selected
33 - matching user_id, matching section, matching recitation
34Switch from view mode to edit mode:
35 - showing visible users
36 - showing selected users
37Switch from edit mode to view and save changes
38Switch from edit mode to view and abandon changes
39Delete users:
40 - visible
41 - selected
42Import users:
43 - replace:
44 - any users
45 - visible users
46 - selected users
47 - no users
48 - add:
49 - any users
50 - no users
51Export 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
16use strict; 62use strict;
17use warnings; 63use warnings;
18use CGI qw(); 64use CGI qw();
19 65use WeBWorK::Utils qw(readFile readDirectory);
66use Apache::Constants qw(:common REDIRECT DONE); #FIXME -- this should be called higher up in the object tree.
20use constant HIDE_USERS_THRESHHOLD => 20; 67use constant HIDE_USERS_THRESHHOLD => 20;
68use constant EDIT_FORMS => [qw(cancelEdit saveEdit)];
69use constant VIEW_FORMS => [qw(filter edit import export add delete)];
70use constant STATE_PARAMS => [qw(user effectiveUser key visible_users no_visible_users prev_visible_users no_prev_visible_users editMode sortField)];
71
72use 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
84use 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};
147sub 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
179sub 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
22sub initialize { 197sub 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
74sub fieldEditHTML {
75 my ($self, $fieldName, $value, $properties) = @_;
76 my $size = $properties->{size};
77 my $type = $properties->{type};
78 my $access = $properties->{access};
79 my $items = $properties->{items};
80 my $synonyms = $properties->{synonyms};
81
82 if ($access eq "readonly") {
83 return $value;
84 }
85
86 if ($type eq "number" or $type eq "text") {
87 return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size});
88 }
89
90 if ($type eq "enumerable") {
91 my $matched = undef; # Whether a synonym match has occurred
92 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 227sub body {
115sub 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
128sub title {
129 my $self = shift;
130 return "User List";
131}
132
133sub 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
148sub 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
463sub 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
475sub 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
495sub 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"
547sub 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
576sub 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
594sub 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
615sub 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
636sub 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}
668sub 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
674sub 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}
679sub 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
717sub 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
755sub 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
790sub 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
820sub cancelEdit_form {
821 my ($self, $onChange, %actionParams) = @_;
822 return "Abandon changes";
823}
824
825sub 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
843sub saveEdit_form {
844 my ($self, $onChange, %actionParams) = @_;
845 return "Save changes";
846}
847
848sub 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
894sub byUserID { $a->user_id cmp $b->user_id }
895sub byFirstName { $a->first_name cmp $b->first_name }
896sub byLastName { $a->last_name cmp $b->last_name }
897sub byEmailAddress { $a->email_address cmp $b->email_address }
898sub byStudentID { $a->student_id cmp $b->student_id }
899sub byStatus { $a->status cmp $b->status }
900sub bySection { $a->section cmp $b->section }
901sub byRecitation { $a->recitation cmp $b->recitation }
902sub byComment { $a->comment cmp $b->comment }
903
904sub byLnFnUid { &byLastName || &byFirstName || &byUserID }
905
906################################################################################
907# utilities
908################################################################################
909
910# generate labels for section/recitation popup menus
911sub 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
924sub 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
1001sub 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
1038sub 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
1078sub 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
1157sub 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
4791; 12211;
1222

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9