Parent Directory
|
Revision Log
Added the ability to change passwords on the classlist page and fixed up sorting a bit. For example commented out sorting by permission which didn't work. I'll work on sorting next fixing the above and allowing profs to sort by clicking on heading as on the Student Progress page. Arnie
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm,v 1.62 2005/01/14 03:15:33 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. 15 ################################################################################ 16 17 package WeBWorK::ContentGenerator::Instructor::UserList; 18 use base qw(WeBWorK::ContentGenerator::Instructor); 19 20 =head1 NAME 21 22 WeBWorK::ContentGenerator::Instructor::UserList - Entry point for User-specific 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 Switch from view mode to password mode: 40 - showing visible users 41 - showing selected users 42 Switch from password mode to view and save changes 43 Switch from password mode to view and abandon changes 44 Delete users: 45 - visible 46 - selected 47 Import users: 48 - replace: 49 - any users 50 - visible users 51 - selected users 52 - no users 53 - add: 54 - any users 55 - no users 56 Export users: 57 - export: 58 - all 59 - visible 60 - selected 61 - to: 62 - existing file on server (overwrite): [ list of files ] 63 - new file on server (create): [ filename ] 64 65 =cut 66 67 use strict; 68 use warnings; 69 use CGI qw(); 70 use WeBWorK::File::Classlist; 71 use WeBWorK::Utils qw(readFile readDirectory cryptPassword); 72 use WeBWorK::Authen qw(checkKey); 73 use Apache::Constants qw(:common REDIRECT DONE); #FIXME -- this should be called higher up in the object tree. 74 use constant HIDE_USERS_THRESHHOLD => 50; 75 use constant EDIT_FORMS => [qw(cancelEdit saveEdit)]; 76 use constant PASSWORD_FORMS => [qw(cancelPassword savePassword)]; 77 use constant VIEW_FORMS => [qw(filter sort edit password import export add delete)]; 78 79 # permissions needed to perform a given action 80 use constant FORM_PERMS => { 81 saveEdit => "modify_student_data", 82 edit => "modify_student_data", 83 savePassword => "change_password", 84 password => "change_password", 85 import => "modify_student_data", 86 export => "modify_classlist_files", 87 add => "modify_student_data", 88 delete => "modify_student_data", 89 }; 90 91 # permissions needed to view a given field 92 use constant FIELD_PERMS => { 93 act_as => "become_student", 94 sets => "assign_problem_sets", 95 }; 96 97 use constant STATE_PARAMS => [qw(user effectiveUser key visible_users no_visible_users prev_visible_users no_prev_visible_users editMode passwordMode primarySortField secondarySortField ternarySortField)]; 98 99 use constant SORT_SUBS => { 100 user_id => \&byUserID, 101 first_name => \&byFirstName, 102 last_name => \&byLastName, 103 email_address => \&byEmailAddress, 104 student_id => \&byStudentID, 105 status => \&byStatus, 106 section => \&bySection, 107 recitation => \&byRecitation, 108 comment => \&byComment, 109 # permission => \&byPermission, 110 }; 111 112 use constant FIELD_PROPERTIES => { 113 user_id => { 114 type => "text", 115 size => 8, 116 access => "readonly", 117 }, 118 first_name => { 119 type => "text", 120 size => 10, 121 access => "readwrite", 122 }, 123 last_name => { 124 type => "text", 125 size => 10, 126 access => "readwrite", 127 }, 128 email_address => { 129 type => "text", 130 size => 20, 131 access => "readwrite", 132 }, 133 student_id => { 134 type => "text", 135 size => 11, 136 access => "readwrite", 137 }, 138 status => { 139 type => "enumerable", 140 size => 4, 141 access => "readwrite", 142 items => { 143 "C" => "Enrolled", 144 "D" => "Drop", 145 "A" => "Audit", 146 }, 147 synonyms => { 148 qr/^[ce]/i => "C", 149 qr/^[dw]/i => "D", 150 qr/^a/i => "A", 151 "*" => "C", 152 } 153 }, 154 section => { 155 type => "text", 156 size => 4, 157 access => "readwrite", 158 }, 159 recitation => { 160 type => "text", 161 size => 4, 162 access => "readwrite", 163 }, 164 comment => { 165 type => "text", 166 size => 20, 167 access => "readwrite", 168 }, 169 permission => { 170 type => "number", 171 size => 2, 172 access => "readwrite", 173 } 174 }; 175 sub pre_header_initialize { 176 my $self = shift; 177 my $r = $self->r; 178 my $urlpath = $r->urlpath; 179 my $authz = $r->authz; 180 my $ce = $r->ce; 181 my $courseName = $urlpath->arg("courseID"); 182 my $user = $r->param('user'); 183 # Handle redirects, if any. 184 ############################## 185 # Redirect to the addUser page 186 ################################## 187 188 # Check permissions 189 return unless $authz->hasPermissions($user, "access_instructor_tools"); 190 191 defined($r->param('action')) && $r->param('action') eq 'add' && do { 192 # fix url and redirect 193 my $root = $ce->{webworkURLs}->{root}; 194 195 my $numberOfStudents = $r->param('number_of_students'); 196 warn "number of students not defined " unless defined $numberOfStudents; 197 198 my $uri=$self->systemLink( $urlpath->newFromModule('WeBWorK::ContentGenerator::Instructor::AddUsers',courseID=>$courseName), 199 params=>{ 200 number_of_students=>$numberOfStudents, 201 } 202 ); 203 #FIXME does the display mode need to be defined? 204 #FIXME url_authen_args also includes an effective user, so the new one must come first. 205 # even that might not work with every browser since there are two effective User assignments. 206 $r->header_out(Location => $uri); 207 $self->{noContent} = 1; # forces redirect 208 return; 209 }; 210 } 211 # FIXME -- this should be moved up to instructor or contentgenerator 212 sub header { 213 my $self = shift; 214 return REDIRECT if $self->{noContent}; 215 my $r = $self->r; 216 $r->content_type('text/html'); 217 $r->send_http_header(); 218 return OK; 219 } 220 221 #FIXME -- this should probably be moved up to instructor or contentgenerator as well 222 #sub nbsp { 223 # my $str = shift; 224 # ($str =~/\S/) ? $str : ' ' ; # returns non-breaking space for empty strings 225 # # tricky cases: $str =0; 226 # # $str is a complex number 227 #} 228 # moved to ContentGenerator.pm 229 230 sub initialize { 231 my ($self) = @_; 232 my $r = $self->r; 233 my $db = $r->db; 234 my $ce = $r->ce; 235 my $authz = $r->authz; 236 my $user = $r->param('user'); 237 238 # Check permissions 239 return unless $authz->hasPermissions($user, "access_instructor_tools"); 240 241 #if (defined($r->param('addStudent'))) { 242 # my $newUser = $db->newUser; 243 # my $newPermissionLevel = $db->newPermissionLevel; 244 # my $newPassword = $db->newPassword; 245 # $newUser->user_id($r->param('newUserID')); 246 # $newPermissionLevel->user_id($r->param('newUserID')); 247 # $newPassword->user_id($r->param('newUserID')); 248 # $newUser->status('C'); 249 # $newPermissionLevel->permission(0); 250 # $db->addUser($newUser); 251 # $db->addPermissionLevel($newPermissionLevel); 252 # $db->addPassword($newPassword); 253 #} 254 } 255 256 257 258 sub body { 259 my ($self) = @_; 260 my $r = $self->r; 261 my $urlpath = $r->urlpath; 262 my $db = $r->db; 263 my $ce = $r->ce; 264 my $authz = $r->authz; 265 my $courseName = $urlpath->arg("courseID"); 266 my $setID = $urlpath->arg("setID"); 267 my $user = $r->param('user'); 268 269 my $root = $ce->{webworkURLs}->{root}; 270 271 # templates for getting field names 272 my $userTemplate = $self->{userTemplate} = $db->newUser; 273 my $permissionLevelTemplate = $self->{permissionLevelTemplate} = $db->newPermissionLevel; 274 275 return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to access the instructor tools.")) 276 unless $authz->hasPermissions($user, "access_instructor_tools"); 277 278 # This table can be consulted when display-ready forms of field names are needed. 279 my %prettyFieldNames = map { $_ => $_ } 280 $userTemplate->FIELDS(), 281 $permissionLevelTemplate->FIELDS(); 282 283 @prettyFieldNames{qw( 284 user_id 285 first_name 286 last_name 287 email_address 288 student_id 289 status 290 section 291 recitation 292 comment 293 permission 294 )} = ( 295 "Login Name", 296 "First Name", 297 "Last Name", 298 "E-mail", 299 "Student ID", 300 "Status", 301 "Section", 302 "Recitation", 303 "Comment", 304 "Perm. Level" 305 ); 306 307 $self->{prettyFieldNames} = \%prettyFieldNames; 308 ########## set initial values for state fields 309 310 my @allUserIDs = $db->listUsers; 311 $self->{totalSets} = $db->listGlobalSets; # save for use in "assigned sets" links 312 $self->{allUserIDs} = \@allUserIDs; 313 314 if (defined $r->param("visible_users")) { 315 $self->{visibleUserIDs} = [ $r->param("visible_users") ]; 316 } elsif (defined $r->param("no_visible_users")) { 317 $self->{visibleUserIDs} = []; 318 } else { 319 if (@allUserIDs > HIDE_USERS_THRESHHOLD) { 320 $self->{visibleUserIDs} = []; 321 } else { 322 $self->{visibleUserIDs} = [ @allUserIDs ]; 323 } 324 } 325 326 $self->{prevVisibleUserIDs} = $self->{visibleUserIDs}; 327 328 if (defined $r->param("selected_users")) { 329 $self->{selectedUserIDs} = [ $r->param("selected_users") ]; 330 } else { 331 $self->{selectedUserIDs} = []; 332 } 333 334 $self->{editMode} = $r->param("editMode") || 0; 335 336 return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify student data")) 337 if $self->{editMode} and not $authz->hasPermissions($user, "modify_student_data"); 338 339 340 $self->{passwordMode} = $r->param("passwordMode") || 0; 341 342 return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify student data")) 343 if $self->{passwordMode} and not $authz->hasPermissions($user, "modify_student_data"); 344 345 346 $self->{primarySortField} = $r->param("primarySortField") || "last_name"; 347 $self->{secondarySortField} = $r->param("secondarySortField") || "first_name"; 348 $self->{ternarySortField} = $r->param("ternarySortField") || "student_id"; 349 350 my @allUsers = $db->getUsers(@allUserIDs); 351 my (%sections, %recitations); 352 foreach my $User (@allUsers) { 353 push @{$sections{defined $User->section ? $User->section : ""}}, $User->user_id; 354 push @{$recitations{defined $User->recitation ? $User->recitation : ""}}, $User->user_id; 355 } 356 $self->{sections} = \%sections; 357 $self->{recitations} = \%recitations; 358 359 ########## call action handler 360 361 my $actionID = $r->param("action"); 362 if ($actionID) { 363 unless (grep { $_ eq $actionID } @{ VIEW_FORMS() }, @{ EDIT_FORMS() }, @{ PASSWORD_FORMS() } ) { 364 die "Action $actionID not found"; 365 } 366 # Check permissions 367 if (not FORM_PERMS()->{$actionID} or $authz->hasPermissions($user, FORM_PERMS()->{$actionID})) { 368 my $actionHandler = "${actionID}_handler"; 369 my %genericParams; 370 foreach my $param (qw(selected_users)) { 371 $genericParams{$param} = [ $r->param($param) ]; 372 } 373 my %actionParams = $self->getActionParams($actionID); 374 my %tableParams = $self->getTableParams(); 375 print CGI::p( 376 '<div style="color:green">', 377 "Result of last action performed: ", 378 CGI::i($self->$actionHandler(\%genericParams, \%actionParams, \%tableParams)), 379 '</div>', 380 CGI::hr() 381 ); 382 } else { 383 return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to perform this action.")); 384 } 385 } 386 387 ########## retrieve possibly changed values for member fields 388 389 #@allUserIDs = @{ $self->{allUserIDs} }; # do we need this one? 390 my @visibleUserIDs = @{ $self->{visibleUserIDs} }; 391 my @prevVisibleUserIDs = @{ $self->{prevVisibleUserIDs} }; 392 my @selectedUserIDs = @{ $self->{selectedUserIDs} }; 393 my $editMode = $self->{editMode}; 394 my $passwordMode = $self->{passwordMode}; 395 my $primarySortField = $self->{primarySortField}; 396 my $secondarySortField = $self->{secondarySortField}; 397 my $ternarySortField = $self->{ternarySortField}; 398 399 #warn "visibleUserIDs=@visibleUserIDs\n"; 400 #warn "prevVisibleUserIDs=@prevVisibleUserIDs\n"; 401 #warn "selectedUserIDs=@selectedUserIDs\n"; 402 #warn "editMode=$editMode\n"; 403 #warn "passwordMode=$passwordMode\n"; 404 405 ########## get required users 406 407 my @Users = grep { defined $_ } @visibleUserIDs ? $db->getUsers(@visibleUserIDs) : (); 408 409 my %sortSubs = %{ SORT_SUBS() }; 410 my $primarySortSub = $sortSubs{$primarySortField}; 411 my $secondarySortSub = $sortSubs{$secondarySortField}; 412 my $ternarySortSub = $sortSubs{$ternarySortField}; 413 414 415 # # don't forget to sort in opposite order of importance 416 # @Users = sort $secondarySortSub @Users; 417 # @Users = sort $primarySortSub @Users; 418 # #@Users = sort byLnFnUid @Users; 419 420 # Always have a definite sort order even if first three sorts don't determine things 421 @Users = sort { 422 &$primarySortSub 423 || 424 &$secondarySortSub 425 || 426 &$ternarySortSub 427 || 428 byLastName 429 || 430 byFirstName 431 || 432 byUserID 433 } 434 @Users; 435 436 my @PermissionLevels; 437 438 for (my $i = 0; $i < @Users; $i++) { 439 my $User = $Users[$i]; 440 my $PermissionLevel = $db->getPermissionLevel($User->user_id); # checked 441 442 unless ($PermissionLevel) { 443 # uh oh! no permission level record found! 444 warn "added missing permission level for user ", $User->user_id, "\n"; 445 446 # create a new permission level record 447 $PermissionLevel = $db->newPermissionLevel; 448 $PermissionLevel->user_id($User->user_id); 449 $PermissionLevel->permission(0); 450 451 # add it to the database 452 $db->addPermissionLevel($PermissionLevel); 453 } 454 455 $PermissionLevels[$i] = $PermissionLevel; 456 } 457 458 ########## print beginning of form 459 460 print CGI::start_form({method=>"post", action=>$self->systemLink($urlpath,authen=>0), name=>"userlist"}); 461 print $self->hidden_authen_fields(); 462 463 ########## print state data 464 465 print "\n<!-- state data here -->\n"; 466 467 if (@visibleUserIDs) { 468 print CGI::hidden(-name=>"visible_users", -value=>\@visibleUserIDs); 469 } else { 470 print CGI::hidden(-name=>"no_visible_users", -value=>"1"); 471 } 472 473 if (@prevVisibleUserIDs) { 474 print CGI::hidden(-name=>"prev_visible_users", -value=>\@prevVisibleUserIDs); 475 } else { 476 print CGI::hidden(-name=>"no_prev_visible_users", -value=>"1"); 477 } 478 479 print CGI::hidden(-name=>"editMode", -value=>$editMode); 480 481 print CGI::hidden(-name=>"passwordMode", -value=>$passwordMode); 482 483 print CGI::hidden(-name=>"primarySortField", -value=>$primarySortField); 484 print CGI::hidden(-name=>"secondarySortField", -value=>$secondarySortField); 485 print CGI::hidden(-name=>"ternarySortField", -value=>$ternarySortField); 486 487 print "\n<!-- state data here -->\n"; 488 489 ########## print action forms 490 491 print CGI::start_table({}); 492 print CGI::Tr({}, CGI::td({-colspan=>2}, "Select an action to perform:")); 493 494 my @formsToShow; 495 if ($editMode) { 496 @formsToShow = @{ EDIT_FORMS() }; 497 }elsif ($passwordMode) { 498 @formsToShow = @{ PASSWORD_FORMS() }; 499 } else { 500 @formsToShow = @{ VIEW_FORMS() }; 501 } 502 503 my $i = 0; 504 foreach my $actionID (@formsToShow) { 505 # Check permissions 506 next if FORM_PERMS()->{$actionID} and not $authz->hasPermissions($user, FORM_PERMS()->{$actionID}); 507 my $actionForm = "${actionID}_form"; 508 my $onChange = "document.userlist.action[$i].checked=true"; 509 my %actionParams = $self->getActionParams($actionID); 510 511 print CGI::Tr({-valign=>"top"}, 512 CGI::td({}, CGI::input({-type=>"radio", -name=>"action", -value=>$actionID})), 513 CGI::td({}, $self->$actionForm($onChange, %actionParams)) 514 ); 515 516 $i++; 517 } 518 519 print CGI::Tr({}, CGI::td({-colspan=>2, -align=>"center"}, 520 CGI::submit(-value=>"Take Action!")) 521 ); 522 print CGI::end_table(); 523 524 ########## print table 525 526 print CGI::p("Showing ", scalar @Users, " out of ", scalar @allUserIDs, " users."); 527 528 print CGI::p("If a password field is left blank, the student's current password will be maintained.") if $passwordMode; 529 530 $self->printTableHTML(\@Users, \@PermissionLevels, \%prettyFieldNames, 531 editMode => $editMode, 532 passwordMode => $passwordMode, 533 selectedUserIDs => \@selectedUserIDs, 534 ); 535 536 537 ########## print end of form 538 539 print CGI::end_form(); 540 541 return ""; 542 } 543 544 ################################################################################ 545 # extract particular params and put them in a hash (values are ARRAYREFs!) 546 ################################################################################ 547 548 sub getActionParams { 549 my ($self, $actionID) = @_; 550 my $r = $self->{r}; 551 552 my %actionParams; 553 foreach my $param ($r->param) { 554 next unless $param =~ m/^action\.$actionID\./; 555 $actionParams{$param} = [ $r->param($param) ]; 556 } 557 return %actionParams; 558 } 559 560 sub getTableParams { 561 my ($self) = @_; 562 my $r = $self->{r}; 563 564 my %tableParams; 565 foreach my $param ($r->param) { 566 next unless $param =~ m/^(?:user|permission)\./; 567 $tableParams{$param} = [ $r->param($param) ]; 568 } 569 return %tableParams; 570 } 571 572 ################################################################################ 573 # actions and action triggers 574 ################################################################################ 575 576 # filter, edit, cancelEdit, and saveEdit should stay with the display module and 577 # not be real "actions". that way, all actions are shown in view mode and no 578 # actions are shown in edit mode. 579 580 sub filter_form { 581 my ($self, $onChange, %actionParams) = @_; 582 #return CGI::table({}, CGI::Tr({-valign=>"top"}, 583 # CGI::td({}, 584 585 my %prettyFieldNames = %{ $self->{prettyFieldNames} }; 586 587 return join("", 588 "Show ", 589 CGI::popup_menu( 590 -name => "action.filter.scope", 591 -values => [qw(all none selected match_regex)], 592 -default => $actionParams{"action.filter.scope"}->[0] || "match_regex", 593 -labels => { 594 all => "all users", 595 none => "no users", 596 selected => "users checked below", 597 # match_ids => "users with matching user IDs:", 598 match_regex => "users who match:", 599 # match_section => "users in selected section", 600 # match_recitation => "users in selected recitation", 601 }, 602 -onchange => $onChange, 603 ), 604 " ", 605 CGI::textfield( 606 -name => "action.filter.user_ids", 607 -value => $actionParams{"action.filter.user_ids"}->[0] || "",, 608 -width => "50", 609 -onchange => $onChange, 610 ), 611 # " (separate multiple IDs with commas)", 612 # CGI::br(), 613 # "sections: ", 614 # CGI::popup_menu( 615 # -name => "action.filter.section", 616 # -values => [ keys %{ $self->{sections} } ], 617 # -default => $actionParams{"action.filter.section"}->[0] || "", 618 # -labels => { $self->menuLabels($self->{sections}) }, 619 # -onchange => $onChange, 620 # ), 621 # " recitations: ", 622 # CGI::popup_menu( 623 # -name => "action.filter.recitation", 624 # -values => [ keys %{ $self->{recitations} } ], 625 # -default => $actionParams{"action.filter.recitation"}->[0] || "", 626 # -labels => { $self->menuLabels($self->{recitations}) }, 627 # -onchange => $onChange, 628 # ), 629 " in their ", 630 CGI::popup_menu( 631 -name => "action.filter.field", 632 -value => [ keys %{ FIELD_PROPERTIES() } ], 633 -default => $actionParams{"action.filter.field"}->[0] || "user_id", 634 -labels => \%prettyFieldNames, 635 -onchange => $onChange, 636 ), 637 ); 638 # ), 639 #)); 640 } 641 642 # this action handler modifies the "visibleUserIDs" field based on the contents 643 # of the "action.filter.scope" parameter and the "selected_users" 644 sub filter_handler { 645 my ($self, $genericParams, $actionParams, $tableParams) = @_; 646 647 my $r = $self->r; 648 my $db = $r->db; 649 650 my $result; 651 652 my $scope = $actionParams->{"action.filter.scope"}->[0]; 653 if ($scope eq "all") { 654 $result = "showing all users"; 655 $self->{visibleUserIDs} = $self->{allUserIDs}; 656 } elsif ($scope eq "none") { 657 $result = "showing no users"; 658 $self->{visibleUserIDs} = []; 659 } elsif ($scope eq "selected") { 660 $result = "showing selected users"; 661 $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref 662 } elsif ($scope eq "match_regex") { 663 $result = "showing matching users"; 664 my $regex = $actionParams->{"action.filter.user_ids"}->[0]; 665 my $field = $actionParams->{"action.filter.field"}->[0]; 666 my @userRecords = $db->getUsers(@{$self->{allUserIDs}}); 667 my @userIDs; 668 foreach my $record (@userRecords) { 669 next unless $record; 670 671 # add permission level to user record hash so we can match it if necessary 672 if ($field eq "permission") { 673 my $permissionLevel = $db->getPermissionLevel($record->user_id); 674 $record->{permission} = $permissionLevel->permission; 675 } 676 push @userIDs, $record->user_id if $record->{$field} =~ /^$regex/i; 677 } 678 $self->{visibleUserIDs} = \@userIDs; 679 } elsif ($scope eq "match_ids") { 680 my @userIDs = split /\s*,\s*/, $actionParams->{"action.filter.user_ids"}->[0]; 681 $self->{visibleUserIDs} = \@userIDs; 682 } elsif ($scope eq "match_section") { 683 my $section = $actionParams->{"action.filter.section"}->[0]; 684 $self->{visibleUserIDs} = $self->{sections}->{$section}; # an arrayref 685 } elsif ($scope eq "match_recitation") { 686 my $recitation = $actionParams->{"action.filter.recitation"}->[0]; 687 $self->{visibleUserIDs} = $self->{recitations}->{$recitation}; # an arrayref 688 } 689 690 return $result; 691 } 692 693 sub sort_form { 694 my ($self, $onChange, %actionParams) = @_; 695 return join ("", 696 "Sort by ", 697 CGI::popup_menu( 698 -name => "action.sort.primary", 699 # -values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)], ## This isn't defined and I don't have time to fix it right now AKP 700 -values => [qw(user_id first_name last_name email_address student_id status section recitation comment)], 701 -default => $actionParams{"action.sort.primary"}->[0] || "last_name", 702 -labels => { 703 user_id => "Login Name", 704 first_name => "First Name", 705 last_name => "Last Name", 706 email_address => "Email address", 707 student_id => "Student ID", 708 status => "Enrollment Status", 709 section => "Section", 710 recitation => "Recitation", 711 comment => "Comment", 712 # permission => "Perm. Level" ## This isn't defined and I don't have time to fix it right now AKP 713 }, 714 -onchange => $onChange, 715 ), 716 ", then by ", 717 CGI::popup_menu( 718 -name => "action.sort.secondary", 719 # -values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)], ## This isn't defined and I don't have time to fix it right now AKP 720 -values => [qw(user_id first_name last_name email_address student_id status section recitation comment)], 721 -default => $actionParams{"action.sort.secondary"}->[0] || "first_name", 722 -labels => { 723 user_id => "Login Name", 724 first_name => "First Name", 725 last_name => "Last Name", 726 email_address => "Email address", 727 student_id => "Student ID", 728 status => "Enrollment Status", 729 section => "Section", 730 recitation => "Recitation", 731 comment => "Comment", 732 # permission => "Perm. Level" 733 }, 734 -onchange => $onChange, 735 ), 736 ", then by ", 737 CGI::popup_menu( 738 -name => "action.sort.ternary", 739 # -values => [qw(user_id first_name last_name email_address student_id status section recitation comment permission)], ## This isn't defined and I don't have time to fix it right now AKP 740 -values => [qw(user_id first_name last_name email_address student_id status section recitation comment)], 741 -default => $actionParams{"action.sort.ternary"}->[0] || "user_id", 742 -labels => { 743 user_id => "Login Name", 744 first_name => "First Name", 745 last_name => "Last Name", 746 email_address => "Email address", 747 student_id => "Student ID", 748 status => "Enrollment Status", 749 section => "Section", 750 recitation => "Recitation", 751 comment => "Comment", 752 # permission => "Perm. Level" 753 }, 754 -onchange => $onChange, 755 ), 756 757 ".", 758 ); 759 } 760 761 sub sort_handler { 762 my ($self, $genericParams, $actionParams, $tableParams) = @_; 763 764 my $primary = $actionParams->{"action.sort.primary"}->[0]; 765 my $secondary = $actionParams->{"action.sort.secondary"}->[0]; 766 my $ternary = $actionParams->{"action.sort.ternary"}->[0]; 767 768 $self->{primarySortField} = $primary; 769 $self->{secondarySortField} = $secondary; 770 $self->{ternarySortField} = $ternary; 771 772 my %names = ( 773 user_id => "Login Name", 774 first_name => "First Name", 775 last_name => "Last Name", 776 email_address => "Email address", 777 student_id => "Student ID", 778 status => "Enrollment Status", 779 section => "Section", 780 recitation => "Recitation", 781 comment => "Comment", 782 permission => "Perm. Level" 783 ); 784 785 return "Users sorted by $names{$primary}, then by $names{$secondary}, then by $names{$ternary}."; 786 } 787 788 sub edit_form { 789 my ($self, $onChange, %actionParams) = @_; 790 791 return join("", 792 "Edit ", 793 CGI::popup_menu( 794 -name => "action.edit.scope", 795 -values => [qw(all visible selected)], 796 -default => $actionParams{"action.edit.scope"}->[0] || "selected", 797 -labels => { 798 all => "all users", 799 visible => "visible users", 800 selected => "selected users" 801 }, 802 -onchange => $onChange, 803 ), 804 ); 805 } 806 807 sub edit_handler { 808 my ($self, $genericParams, $actionParams, $tableParams) = @_; 809 810 my $result; 811 812 my $scope = $actionParams->{"action.edit.scope"}->[0]; 813 if ($scope eq "all") { 814 $result = "editing all users"; 815 $self->{visibleUserIDs} = $self->{allUserIDs}; 816 } elsif ($scope eq "visible") { 817 $result = "editing visible users"; 818 # leave visibleUserIDs alone 819 } elsif ($scope eq "selected") { 820 $result = "editing selected users"; 821 $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref 822 } 823 $self->{editMode} = 1; 824 825 return $result; 826 } 827 828 829 sub password_form { 830 my ($self, $onChange, %actionParams) = @_; 831 832 return join("", 833 "Give new password to ", 834 CGI::popup_menu( 835 -name => "action.password.scope", 836 -values => [qw(all visible selected)], 837 -default => $actionParams{"action.password.scope"}->[0] || "selected", 838 -labels => { 839 all => "all users", 840 visible => "visible users", 841 selected => "selected users" 842 }, 843 -onchange => $onChange, 844 ), 845 ); 846 } 847 848 sub password_handler { 849 my ($self, $genericParams, $actionParams, $tableParams) = @_; 850 851 my $result; 852 853 my $scope = $actionParams->{"action.password.scope"}->[0]; 854 if ($scope eq "all") { 855 $result = "giving new passwords to all users"; 856 $self->{visibleUserIDs} = $self->{allUserIDs}; 857 } elsif ($scope eq "visible") { 858 $result = "giving new passwords to visible users"; 859 # leave visibleUserIDs alone 860 } elsif ($scope eq "selected") { 861 $result = "giving new passwords to selected users"; 862 $self->{visibleUserIDs} = $genericParams->{selected_users}; # an arrayref 863 } 864 $self->{passwordMode} = 1; 865 866 return $result; 867 } 868 869 sub delete_form { 870 my ($self, $onChange, %actionParams) = @_; 871 872 return join("", 873 CGI::div({class=>"ResultsWithError"}, 874 "Delete ", 875 CGI::popup_menu( 876 -name => "action.delete.scope", 877 -values => [qw(none selected)], 878 -default => $actionParams{"action.delete.scope"}->[0] || "none", 879 -labels => { 880 none => "no users.", 881 #visible => "visible users.", 882 selected => "selected users." 883 }, 884 -onchange => $onChange, 885 ), 886 CGI::em(" Deletion destroys all user-related data and is not undoable!"), 887 ), 888 ); 889 } 890 891 sub delete_handler { 892 my ($self, $genericParams, $actionParams, $tableParams) = @_; 893 my $r = $self->r; 894 my $db = $r->db; 895 my $user = $r->param('user'); 896 my $scope = $actionParams->{"action.delete.scope"}->[0]; 897 898 my @userIDsToDelete = (); 899 #if ($scope eq "visible") { 900 # @userIDsToDelete = @{ $self->{visibleUserIDs} }; 901 #} elsif ($scope eq "selected") { 902 if ($scope eq "selected") { 903 @userIDsToDelete = @{ $self->{selectedUserIDs} }; 904 } 905 906 my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} }; 907 my %visibleUserIDs = map { $_ => 1 } @{ $self->{visibleUserIDs} }; 908 my %selectedUserIDs = map { $_ => 1 } @{ $self->{selectedUserIDs} }; 909 910 my $error = ""; 911 my $num = 0; 912 foreach my $userID (@userIDsToDelete) { 913 if ($user eq $userID) { # don't delete yourself!! 914 $error = "You cannot delete yourself!"; 915 next; 916 } 917 delete $allUserIDs{$userID}; 918 delete $visibleUserIDs{$userID}; 919 delete $selectedUserIDs{$userID}; 920 $db->deleteUser($userID); 921 $num++; 922 } 923 924 $self->{allUserIDs} = [ keys %allUserIDs ]; 925 $self->{visibleUserIDs} = [ keys %visibleUserIDs ]; 926 $self->{selectedUserIDs} = [ keys %selectedUserIDs ]; 927 928 return "deleted $num user" . ($num == 1 ? "" : "s. ") . $error; 929 } 930 sub add_form { 931 my ($self, $onChange, %actionParams) = @_; 932 933 return "Add ", CGI::input({name=>'number_of_students', value=>1,size => 3}), " student(s). "; 934 } 935 936 sub add_handler { 937 my ($self, $genericParams, $actionParams, $tableParams) = @_; 938 # This action is redirected to the addUser.pm module using ../instructor/add_user/... 939 return "Nothing done by add student handler"; 940 } 941 sub import_form { 942 my ($self, $onChange, %actionParams) = @_; 943 return join(" ", 944 "Import users from file", 945 CGI::popup_menu( 946 -name => "action.import.source", 947 -values => [ $self->getCSVList() ], 948 -default => $actionParams{"action.import.source"}->[0] || "", 949 -onchange => $onChange, 950 ), 951 "replacing", 952 CGI::popup_menu( 953 -name => "action.import.replace", 954 -values => [qw(any visible selected none)], 955 -default => $actionParams{"action.import.replace"}->[0] || "none", 956 -labels => { 957 any => "any", 958 visible => "visible", 959 selected => "selected", 960 none => "no", 961 }, 962 -onchange => $onChange, 963 ), 964 "existing users and adding", 965 CGI::popup_menu( 966 -name => "action.import.add", 967 -values => [qw(any none)], 968 -default => $actionParams{"action.import.add"}->[0] || "any", 969 -labels => { 970 any => "any", 971 none => "no", 972 }, 973 -onchange => $onChange, 974 ), 975 "new users", 976 ); 977 } 978 979 sub import_handler { 980 my ($self, $genericParams, $actionParams, $tableParams) = @_; 981 982 my $source = $actionParams->{"action.import.source"}->[0]; 983 my $add = $actionParams->{"action.import.add"}->[0]; 984 my $replace = $actionParams->{"action.import.replace"}->[0]; 985 986 my $fileName = $source; 987 my $createNew = $add eq "any"; 988 my $replaceExisting; 989 my @replaceList; 990 if ($replace eq "any") { 991 $replaceExisting = "any"; 992 } elsif ($replace eq "none") { 993 $replaceExisting = "none"; 994 } elsif ($replace eq "visible") { 995 $replaceExisting = "listed"; 996 @replaceList = @{ $self->{visibleUserIDs} }; 997 } elsif ($replace eq "selected") { 998 $replaceExisting = "listed"; 999 @replaceList = @{ $self->{selectedUserIDs} }; 1000 } 1001 1002 my ($replaced, $added, $skipped) 1003 = $self->importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList); 1004 1005 # make new users visible... do we really want to do this? probably. 1006 push @{ $self->{visibleUserIDs} }, @$added; 1007 1008 my $numReplaced = @$replaced; 1009 my $numAdded = @$added; 1010 my $numSkipped = @$skipped; 1011 1012 return $numReplaced . " user" . ($numReplaced == 1 ? "" : "s") . " replaced, " 1013 . $numAdded . " user" . ($numAdded == 1 ? "" : "s") . " added, " 1014 . $numSkipped . " user" . ($numSkipped == 1 ? "" : "s") . " skipped" 1015 . " (" . join (", ", @$skipped) . ") "; 1016 } 1017 1018 sub export_form { 1019 my ($self, $onChange, %actionParams) = @_; 1020 return join("", 1021 "Export ", 1022 CGI::popup_menu( 1023 -name => "action.export.scope", 1024 -values => [qw(all visible selected)], 1025 -default => $actionParams{"action.export.scope"}->[0] || "visible", 1026 -labels => { 1027 all => "all users", 1028 visible => "visible users", 1029 selected => "selected users" 1030 }, 1031 -onchange => $onChange, 1032 ), 1033 " to ", 1034 CGI::popup_menu( 1035 -name=>"action.export.target", 1036 -values => [ "new", $self->getCSVList() ], 1037 -labels => { new => "a new file named:" }, 1038 -default => $actionParams{"action.export.target"}->[0] || "", 1039 -onchange => $onChange, 1040 ), 1041 #CGI::br(), 1042 #"new file to create: ", 1043 CGI::textfield( 1044 -name => "action.export.new", 1045 -value => $actionParams{"action.export.new"}->[0] || "",, 1046 -width => "50", 1047 -onchange => $onChange, 1048 ), 1049 CGI::tt(".lst"), 1050 ); 1051 } 1052 1053 sub export_handler { 1054 my ($self, $genericParams, $actionParams, $tableParams) = @_; 1055 1056 my $scope = $actionParams->{"action.export.scope"}->[0]; 1057 my $target = $actionParams->{"action.export.target"}->[0]; 1058 my $new = $actionParams->{"action.export.new"}->[0]; 1059 1060 my $fileName; 1061 if ($target eq "new") { 1062 $fileName = $new; 1063 } else { 1064 $fileName = $target; 1065 } 1066 1067 $fileName .= ".lst" unless $fileName =~ m/\.lst$/; 1068 1069 my @userIDsToExport; 1070 if ($scope eq "all") { 1071 @userIDsToExport = @{ $self->{allUserIDs} }; 1072 } elsif ($scope eq "visible") { 1073 @userIDsToExport = @{ $self->{visibleUserIDs} }; 1074 } elsif ($scope eq "selected") { 1075 @userIDsToExport = @{ $self->{selectedUserIDs} }; 1076 } 1077 1078 $self->exportUsersToCSV($fileName, @userIDsToExport); 1079 1080 return scalar @userIDsToExport . " users exported"; 1081 } 1082 1083 sub cancelEdit_form { 1084 my ($self, $onChange, %actionParams) = @_; 1085 return "Abandon changes"; 1086 } 1087 1088 sub cancelEdit_handler { 1089 my ($self, $genericParams, $actionParams, $tableParams) = @_; 1090 my $r = $self->r; 1091 1092 #$self->{selectedUserIDs} = $self->{visibleUserIDs}; 1093 # only do the above if we arrived here via "edit selected users" 1094 if (defined $r->param("prev_visible_users")) { 1095 $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ]; 1096 } elsif (defined $r->param("no_prev_visible_users")) { 1097 $self->{visibleUserIDs} = []; 1098 } else { 1099 # leave it alone 1100 } 1101 $self->{editMode} = 0; 1102 1103 return "changes abandoned"; 1104 } 1105 1106 sub saveEdit_form { 1107 my ($self, $onChange, %actionParams) = @_; 1108 return "Save changes"; 1109 } 1110 1111 sub saveEdit_handler { 1112 my ($self, $genericParams, $actionParams, $tableParams) = @_; 1113 my $r = $self->r; 1114 my $db = $r->db; 1115 1116 my @visibleUserIDs = @{ $self->{visibleUserIDs} }; 1117 foreach my $userID (@visibleUserIDs) { 1118 my $User = $db->getUser($userID); # checked 1119 die "record for visible user $userID not found" unless $User; 1120 my $PermissionLevel = $db->getPermissionLevel($userID); # checked 1121 die "permissions for $userID not defined" unless defined $PermissionLevel; 1122 foreach my $field ($User->NONKEYFIELDS()) { 1123 my $param = "user.${userID}.${field}"; 1124 if (defined $tableParams->{$param}->[0]) { 1125 $User->$field($tableParams->{$param}->[0]); 1126 } 1127 } 1128 1129 foreach my $field ($PermissionLevel->NONKEYFIELDS()) { 1130 my $param = "permission.${userID}.${field}"; 1131 if (defined $tableParams->{$param}->[0]) { 1132 $PermissionLevel->$field($tableParams->{$param}->[0]); 1133 } 1134 } 1135 1136 $db->putUser($User); 1137 $db->putPermissionLevel($PermissionLevel); 1138 } 1139 1140 if (defined $r->param("prev_visible_users")) { 1141 $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ]; 1142 } elsif (defined $r->param("no_prev_visible_users")) { 1143 $self->{visibleUserIDs} = []; 1144 } else { 1145 # leave it alone 1146 } 1147 1148 $self->{editMode} = 0; 1149 1150 return "changes saved"; 1151 } 1152 1153 sub cancelPassword_form { 1154 my ($self, $onChange, %actionParams) = @_; 1155 return "Abandon changes"; 1156 } 1157 1158 sub cancelPassword_handler { 1159 my ($self, $genericParams, $actionParams, $tableParams) = @_; 1160 my $r = $self->r; 1161 1162 #$self->{selectedUserIDs} = $self->{visibleUserIDs}; 1163 # only do the above if we arrived here via "edit selected users" 1164 if (defined $r->param("prev_visible_users")) { 1165 $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ]; 1166 } elsif (defined $r->param("no_prev_visible_users")) { 1167 $self->{visibleUserIDs} = []; 1168 } else { 1169 # leave it alone 1170 } 1171 $self->{passwordMode} = 0; 1172 1173 return "changes abandoned"; 1174 } 1175 1176 sub savePassword_form { 1177 my ($self, $onChange, %actionParams) = @_; 1178 return "Save changes"; 1179 } 1180 1181 sub savePassword_handler { 1182 my ($self, $genericParams, $actionParams, $tableParams) = @_; 1183 my $r = $self->r; 1184 my $db = $r->db; 1185 1186 my @visibleUserIDs = @{ $self->{visibleUserIDs} }; 1187 foreach my $userID (@visibleUserIDs) { 1188 my $User = $db->getUser($userID); # checked 1189 die "record for visible user $userID not found" unless $User; 1190 my $param = "user.${userID}.new_password"; 1191 if ((defined $tableParams->{$param}->[0]) and ($tableParams->{$param}->[0])) { 1192 my $newP = $tableParams->{$param}->[0]; 1193 my $Password = eval {$db->getPassword($User->user_id)}; # checked 1194 my $cryptPassword = cryptPassword($newP); 1195 $Password->password(cryptPassword($newP)); 1196 eval { $db->putPassword($Password) }; 1197 } 1198 } 1199 1200 if (defined $r->param("prev_visible_users")) { 1201 $self->{visibleUserIDs} = [ $r->param("prev_visible_users") ]; 1202 } elsif (defined $r->param("no_prev_visible_users")) { 1203 $self->{visibleUserIDs} = []; 1204 } else { 1205 # leave it alone 1206 } 1207 1208 $self->{passwordMode} = 0; 1209 1210 return "new passwords saved"; 1211 } 1212 1213 1214 ################################################################################ 1215 # sorts 1216 ################################################################################ 1217 1218 sub byUserID { lc $a->user_id cmp lc $b->user_id } 1219 sub byFirstName { (defined $a->first_name && defined $b->first_name) ? lc $a->first_name cmp lc $b->first_name : 0; } 1220 sub byLastName { (defined $a->last_name && defined $b->last_name ) ? lc $a->last_name cmp lc $b->last_name : 0; } 1221 sub byEmailAddress { lc $a->email_address cmp lc $b->email_address } 1222 sub byStudentID { lc $a->student_id cmp lc $b->student_id } 1223 sub byStatus { lc $a->status cmp lc $b->status } 1224 sub bySection { lc $a->section cmp lc $b->section } 1225 sub byRecitation { lc $a->recitation cmp lc $b->recitation } 1226 sub byComment { lc $a->comment cmp lc $b->comment } 1227 #sub byPermission { $a->permission <=> $b->permission } 1228 1229 # sub byLnFnUid { &byLastName || &byFirstName || &byUserID } 1230 1231 ################################################################################ 1232 # utilities 1233 ################################################################################ 1234 1235 # generate labels for section/recitation popup menus 1236 sub menuLabels { 1237 my ($self, $hashRef) = @_; 1238 my %hash = %$hashRef; 1239 1240 my %result; 1241 foreach my $key (keys %hash) { 1242 my $count = @{ $hash{$key} }; 1243 my $displayKey = $key || "<none>"; 1244 $result{$key} = "$displayKey ($count users)"; 1245 } 1246 return %result; 1247 } 1248 1249 sub importUsersFromCSV { 1250 my ($self, $fileName, $createNew, $replaceExisting, @replaceList) = @_; 1251 my $r = $self->r; 1252 my $ce = $r->ce; 1253 my $db = $r->db; 1254 my $dir = $ce->{courseDirs}->{templates}; 1255 my $user = $r->param('user'); 1256 1257 die "illegal character in input: '/'" if $fileName =~ m|/|; 1258 die "won't be able to read from file $dir/$fileName: does it exist? is it readable?" 1259 unless -r "$dir/$fileName"; 1260 1261 my %allUserIDs = map { $_ => 1 } @{ $self->{allUserIDs} }; 1262 my %replaceOK; 1263 if ($replaceExisting eq "none") { 1264 %replaceOK = (); 1265 } elsif ($replaceExisting eq "listed") { 1266 %replaceOK = map { $_ => 1 } @replaceList; 1267 } elsif ($replaceExisting eq "any") { 1268 %replaceOK = %allUserIDs; 1269 } 1270 1271 my (@replaced, @added, @skipped); 1272 1273 # get list of hashrefs representing lines in classlist file 1274 my @classlist = parse_classlist("$dir/$fileName"); 1275 1276 foreach my $record (@classlist) { 1277 my %record = %$record; 1278 my $user_id = $record{user_id}; 1279 1280 if ($user_id eq $user) { # don't replace yourself!! 1281 push @skipped, $user_id; 1282 next; 1283 } 1284 1285 if (exists $allUserIDs{$user_id} and not exists $replaceOK{$user_id}) { 1286 push @skipped, $user_id; 1287 next; 1288 } 1289 1290 if (not exists $allUserIDs{$user_id} and not $createNew) { 1291 push @skipped, $user_id; 1292 next; 1293 } 1294 1295 my $User = $db->newUser(%record); 1296 my $PermissionLevel = $db->newPermissionLevel(user_id => $user_id, permission => 0); 1297 my $Password = $db->newPassword(user_id => $user_id, password => cryptPassword($record{student_id})); 1298 1299 # use password and permission from record if there 1300 if (exists $record{permission}) { 1301 $PermissionLevel->permission($record{permission}); 1302 } 1303 1304 if (exists $record{password}) { 1305 $Password->password($record{password}); 1306 } 1307 1308 if (exists $allUserIDs{$user_id}) { 1309 $db->putUser($User); 1310 $db->putPermissionLevel($PermissionLevel); 1311 $db->putPassword($Password); 1312 push @replaced, $user_id; 1313 } else { 1314 $db->addUser($User); 1315 $db->addPermissionLevel($PermissionLevel); 1316 $db->addPassword($Password); 1317 push @added, $user_id; 1318 } 1319 } 1320 1321 return \@replaced, \@added, \@skipped; 1322 } 1323 1324 sub exportUsersToCSV { 1325 my ($self, $fileName, @userIDsToExport) = @_; 1326 my $r = $self->r; 1327 my $ce = $r->ce; 1328 my $db = $r->db; 1329 my $dir = $ce->{courseDirs}->{templates}; 1330 1331 die "illegal character in input: '/'" if $fileName =~ m|/|; 1332 1333 my @records; 1334 1335 my @Users = $db->getUsers(@userIDsToExport); 1336 my @Passwords = $db->getPasswords(@userIDsToExport); 1337 my @PermissionLevels = $db->getPermissionLevels(@userIDsToExport); 1338 foreach my $i (0 .. $#userIDsToExport) { 1339 my $User = $Users[$i]; 1340 my $Password = $Passwords[$i]; 1341 my $PermissionLevel = $PermissionLevels[$i]; 1342 next unless defined $User; 1343 my %record = ( 1344 defined $PermissionLevel ? $PermissionLevel->toHash : (), 1345 defined $Password ? $Password->toHash : (), 1346 $User->toHash, 1347 ); 1348 push @records, \%record; 1349 } 1350 1351 write_classlist("$dir/$fileName", @records); 1352 } 1353 1354 ################################################################################ 1355 # "display" methods 1356 ################################################################################ 1357 1358 sub fieldEditHTML { 1359 my ($self, $fieldName, $value, $properties) = @_; 1360 my $size = $properties->{size}; 1361 my $type = $properties->{type}; 1362 my $access = $properties->{access}; 1363 my $items = $properties->{items}; 1364 my $synonyms = $properties->{synonyms}; 1365 1366 if ($access eq "readonly") { 1367 return $value; 1368 } 1369 1370 if ($type eq "number" or $type eq "text") { 1371 return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size}); 1372 } 1373 1374 if ($type eq "enumerable") { 1375 my $matched = undef; # Whether a synonym match has occurred 1376 1377 # Process synonyms for enumerable objects 1378 foreach my $synonym (keys %$synonyms) { 1379 if ($synonym ne "*" and $value =~ m/$synonym/) { 1380 $value = $synonyms->{$synonym}; 1381 $matched = 1; 1382 } 1383 } 1384 1385 if (!$matched and exists $synonyms->{"*"}) { 1386 $value = $synonyms->{"*"}; 1387 } 1388 1389 return CGI::popup_menu({ 1390 name => $fieldName, 1391 values => [keys %$items], 1392 default => $value, 1393 labels => $items, 1394 }); 1395 } 1396 } 1397 1398 sub recordEditHTML { 1399 my ($self, $User, $PermissionLevel, %options) = @_; 1400 my $r = $self->r; 1401 my $urlpath = $r->urlpath; 1402 my $db = $r->db; 1403 my $ce = $r->ce; 1404 my $authz = $r->authz; 1405 my $user = $r->param('user'); 1406 my $root = $ce->{webworkURLs}->{root}; 1407 my $courseName = $urlpath->arg("courseID"); 1408 1409 my $editMode = $options{editMode}; 1410 my $passwordMode = $options{passwordMode}; 1411 my $userSelected = $options{userSelected}; 1412 1413 my $statusClass = $ce->{siteDefaults}->{status}->{$User->{status}}; 1414 1415 my $sets = $db->countUserSets($User->user_id); 1416 my $totalSets = $self->{totalSets}; 1417 1418 my $changeEUserURL = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}), 1419 params => {effectiveUser => $User->user_id} 1420 ); 1421 1422 my $setsAssignedToUserURL = $self->systemLink($urlpath->new(type=>'instructor_sets_assigned_to_user', 1423 args=>{courseID => $courseName, 1424 userID => $User->user_id 1425 }), 1426 params => {effectiveUser => $User->user_id} 1427 ); 1428 1429 my $userListURL = $self->systemLink($urlpath->new(type=>'instructor_user_list', args=>{courseID => $courseName} )) . "&editMode=1&visible_users=" . $User->user_id; 1430 1431 my $imageURL = $ce->{webworkURLs}->{htdocs}."/images/edit.gif"; 1432 my $imageLink = CGI::a({href => $userListURL}, CGI::img({src=>$imageURL, border=>0})); 1433 1434 my @tableCells; 1435 1436 # Select 1437 if ($editMode or $passwordMode) { 1438 # column not there 1439 } else { 1440 # selection checkbox 1441 push @tableCells, CGI::checkbox( 1442 -name => "selected_users", 1443 -value => $User->user_id, 1444 -checked => $userSelected, 1445 -label => "", 1446 ); 1447 } 1448 1449 # Act As 1450 if ($editMode or $passwordMode) { 1451 # column not there 1452 } else { 1453 # selection checkbox 1454 if ( FIELD_PERMS()->{act_as} and not $authz->hasPermissions($user, FIELD_PERMS()->{act_as}) ){ 1455 push @tableCells, $User->user_id . $imageLink; 1456 } else { 1457 push @tableCells, CGI::a({href=>$changeEUserURL}, $User->user_id) . $imageLink; 1458 } 1459 } 1460 1461 # Login Status 1462 if ($editMode or $passwordMode) { 1463 # column not there 1464 } else { 1465 # check to see if a user is currently logged in 1466 my $Key = $db->getKey($User->user_id); 1467 push @tableCells, ($Key and WeBWorK::Authen::checkKey($self, $User->user_id, $Key->key)) ? CGI::b("active") : CGI::em("inactive"); 1468 } 1469 1470 # change password (only in password mode) 1471 if ($passwordMode) { 1472 if ($User->user_id eq $user) { 1473 push @tableCells, '' # don't allow a professor to change their own password from this form 1474 } 1475 else { 1476 my $fieldName = 'user.' . $User->user_id . '.' . 'new_password'; 1477 push @tableCells, CGI::input({type=>"text", name=>$fieldName, size=>14});; 1478 } 1479 } 1480 # User ID (edit mode) or Assigned Sets (otherwise) 1481 if ($editMode or $passwordMode) { 1482 # straight user ID 1483 push @tableCells, CGI::div({class=>$statusClass}, $User->user_id); 1484 } else { 1485 # "edit sets assigned to user" link 1486 #push @tableCells, CGI::a({href=>$setsAssignedToUserURL}, "Edit sets"); 1487 if ( FIELD_PERMS()->{sets} and not $authz->hasPermissions($user, FIELD_PERMS()->{sets}) ) { 1488 push @tableCells, "$sets/$totalSets"; 1489 } else { 1490 push @tableCells, CGI::a({href=>$setsAssignedToUserURL}, "$sets/$totalSets"); 1491 } 1492 } 1493 1494 # User Fields 1495 foreach my $field ($User->NONKEYFIELDS) { 1496 my $fieldName = "user." . $User->user_id . "." . $field, 1497 my $fieldValue = $User->$field; 1498 my %properties = %{ FIELD_PROPERTIES()->{$field} }; 1499 $properties{access} = "readonly" unless $editMode; 1500 $fieldValue = $self->nbsp($fieldValue) unless $editMode; 1501 push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties)); 1502 } 1503 1504 # PermissionLevel Fields 1505 foreach my $field ($PermissionLevel->NONKEYFIELDS) { 1506 my $fieldName = "permission." . $PermissionLevel->user_id . "." . $field, 1507 my $fieldValue = $PermissionLevel->$field; 1508 my %properties = %{ FIELD_PROPERTIES()->{$field} }; 1509 $properties{access} = "readonly" unless $editMode; 1510 $fieldValue = $self->nbsp($fieldValue) unless $editMode; 1511 push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties)); 1512 } 1513 1514 return CGI::Tr({}, CGI::td({nowrap=>1}, \@tableCells)); 1515 } 1516 1517 sub printTableHTML { 1518 my ($self, $UsersRef, $PermissionLevelsRef, $fieldNamesRef, %options) = @_; 1519 my $r = $self->r; 1520 my $userTemplate = $self->{userTemplate}; 1521 my $permissionLevelTemplate = $self->{permissionLevelTemplate}; 1522 my @Users = @$UsersRef; 1523 my @PermissionLevels = @$PermissionLevelsRef; 1524 my %fieldNames = %$fieldNamesRef; 1525 1526 my $editMode = $options{editMode}; 1527 my $passwordMode = $options{passwordMode}; 1528 my %selectedUserIDs = map { $_ => 1 } @{ $options{selectedUserIDs} }; 1529 my $currentSort = $options{currentSort}; 1530 1531 # names of headings: 1532 my @realFieldNames = ( 1533 $userTemplate->KEYFIELDS, 1534 $userTemplate->NONKEYFIELDS, 1535 $permissionLevelTemplate->NONKEYFIELDS, 1536 ); 1537 1538 my %sortSubs = %{ SORT_SUBS() }; 1539 #my @stateParams = @{ STATE_PARAMS() }; 1540 #my $hrefPrefix = $r->uri . "?" . $self->url_args(@stateParams); # $self->url_authen_args 1541 my @tableHeadings; 1542 foreach my $field (@realFieldNames) { 1543 my $result = $fieldNames{$field}; 1544 push @tableHeadings, $result; 1545 }; 1546 1547 # prepend selection checkbox? only if we're NOT editing! 1548 unless($editMode or $passwordMode) { 1549 shift @tableHeadings; # Remove user id 1550 unshift @tableHeadings, "Select", "Act As", "Login Status", "Assigned Sets"; 1551 } 1552 if($passwordMode) { 1553 unshift @tableHeadings, "New Password"; 1554 } 1555 1556 # print the table 1557 if ($editMode or $passwordMode) { 1558 print CGI::start_table({}); 1559 } else { 1560 print CGI::start_table({-border=>1, -nowrap=>1}); 1561 } 1562 1563 print CGI::Tr({}, CGI::th({}, \@tableHeadings)); 1564 1565 1566 for (my $i = 0; $i < @Users; $i++) { 1567 my $User = $Users[$i]; 1568 my $PermissionLevel = $PermissionLevels[$i]; 1569 1570 print $self->recordEditHTML($User, $PermissionLevel, 1571 editMode => $editMode, 1572 passwordMode => $passwordMode, 1573 userSelected => exists $selectedUserIDs{$User->user_id} 1574 ); 1575 } 1576 1577 print CGI::end_table(); 1578 ######################################### 1579 # if there are no users shown print message 1580 # 1581 ########################################## 1582 1583 print CGI::p( 1584 CGI::i("No students shown. Choose one of the options above to 1585 list the students in the course.") 1586 ) unless @Users; 1587 } 1588 1589 1; 1590
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |