| 1 | ################################################################################ |
1 | ################################################################################ |
| 2 | # WeBWorK Online Homework Delivery System |
2 | # WeBWorK Online Homework Delivery System |
| 3 | # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/ |
3 | # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/ |
| 4 | # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v 1.60 2006/11/10 17:55:55 sh002i Exp $ |
4 | # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v 1.57.2.1 2006/11/10 17:59:09 sh002i Exp $ |
| 5 | # |
5 | # |
| 6 | # This program is free software; you can redistribute it and/or modify it under |
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 |
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 |
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. |
9 | # version, or (b) the "Artistic License" which comes with this package. |
| … | |
… | |
| 25 | |
25 | |
| 26 | use strict; |
26 | use strict; |
| 27 | use warnings; |
27 | use warnings; |
| 28 | #use CGI qw(-nosticky ); |
28 | #use CGI qw(-nosticky ); |
| 29 | use WeBWorK::CGI; |
29 | use WeBWorK::CGI; |
|
|
30 | use Data::Dumper; |
| 30 | use File::Temp qw/tempfile/; |
31 | use File::Temp qw/tempfile/; |
| 31 | use WeBWorK::CourseEnvironment; |
32 | use WeBWorK::CourseEnvironment; |
| 32 | use IO::File; |
33 | use IO::File; |
| 33 | use String::ShellQuote; |
|
|
| 34 | use WeBWorK::Debug; |
34 | use WeBWorK::Debug; |
| 35 | use WeBWorK::Utils qw(cryptPassword writeLog listFilesRecursive); |
35 | use WeBWorK::Utils qw(cryptPassword writeLog listFilesRecursive); |
| 36 | use WeBWorK::Utils::CourseManagement qw(addCourse renameCourse deleteCourse listCourses archiveCourse |
36 | use WeBWorK::Utils::CourseManagement qw(addCourse renameCourse deleteCourse listCourses archiveCourse |
| 37 | listArchivedCourses unarchiveCourse); |
37 | listArchivedCourses unarchiveCourse); |
| 38 | use WeBWorK::Utils::DBImportExport qw(dbExport dbImport); |
38 | use WeBWorK::Utils::DBImportExport qw(dbExport dbImport); |
|
|
39 | # needed for location management |
|
|
40 | use Net::IP; |
| 39 | |
41 | |
| 40 | use constant IMPORT_EXPORT_WARNING => "The ability to import and export |
42 | use constant IMPORT_EXPORT_WARNING => "The ability to import and export |
| 41 | databases is still under development. It seems to work but it is <b>VERY</b> |
43 | databases is still under development. It seems to work but it is <b>VERY</b> |
| 42 | slow on large courses. You may prefer to use webwork2/bin/wwdb or the mysql |
44 | slow on large courses. You may prefer to use webwork2/bin/wwdb or the mysql |
| 43 | dump facility for archiving large courses. Please send bug reports if you find |
45 | dump facility for archiving large courses. Please send bug reports if you find |
| … | |
… | |
| 207 | } else { |
209 | } else { |
| 208 | # form only |
210 | # form only |
| 209 | $method_to_call = "unarchive_course_form"; |
211 | $method_to_call = "unarchive_course_form"; |
| 210 | } |
212 | } |
| 211 | } |
213 | } |
|
|
214 | elsif ($subDisplay eq "manage_locations") { |
|
|
215 | if (defined ($r->param("manage_location_action"))) { |
|
|
216 | $method_to_call = |
|
|
217 | $r->param("manage_location_action"); |
|
|
218 | } |
|
|
219 | else{ |
|
|
220 | $method_to_call = "manage_location_form"; |
|
|
221 | } |
|
|
222 | } |
| 212 | else { |
223 | else { |
| 213 | @errors = "Unrecognized sub-display @{[ CGI::b($subDisplay) ]}."; |
224 | @errors = "Unrecognized sub-display @{[ CGI::b($subDisplay) ]}."; |
| 214 | } |
225 | } |
| 215 | |
|
|
| 216 | } |
226 | } |
| 217 | |
227 | |
| 218 | $self->{errors} = \@errors; |
228 | $self->{errors} = \@errors; |
| 219 | $self->{method_to_call} = $method_to_call; |
229 | $self->{method_to_call} = $method_to_call; |
| 220 | } |
230 | } |
| … | |
… | |
| 298 | CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"import_database"})}, "Import Database"), |
308 | CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"import_database"})}, "Import Database"), |
| 299 | " | ", |
309 | " | ", |
| 300 | CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"archive_course"})}, "Archive Course"), |
310 | CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"archive_course"})}, "Archive Course"), |
| 301 | "|", |
311 | "|", |
| 302 | CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"unarchive_course"})}, "Unarchive Course"), |
312 | CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"unarchive_course"})}, "Unarchive Course"), |
|
|
313 | "|", |
|
|
314 | CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"manage_locations"})}, "Manage Locations"), |
| 303 | CGI::hr(), |
315 | CGI::hr(), |
| 304 | $methodMessage, |
316 | $methodMessage, |
| 305 | |
317 | |
| 306 | ); |
318 | ); |
| 307 | |
319 | |
| … | |
… | |
| 648 | |
660 | |
| 649 | my @users; |
661 | my @users; |
| 650 | |
662 | |
| 651 | # copy users from current (admin) course if desired |
663 | # copy users from current (admin) course if desired |
| 652 | if ($add_admin_users ne "") { |
664 | if ($add_admin_users ne "") { |
| 653 | # DBFIXME do the searching in the database, and grab user/passwd/perm. all at once with a join |
|
|
| 654 | foreach my $userID ($db->listUsers) { |
665 | foreach my $userID ($db->listUsers) { |
| 655 | if ($userID eq $add_initial_userID) { |
666 | if ($userID eq $add_initial_userID) { |
| 656 | $self->addbadmessage( "User '$userID' will not be copied from admin course as it is the initial instructor."); |
667 | $self->addbadmessage( "User '$userID' will not be copied from admin course as it is the initial instructor."); |
| 657 | next; |
668 | next; |
| 658 | } |
669 | } |
| … | |
… | |
| 752 | permission => "0", |
763 | permission => "0", |
| 753 | ); |
764 | ); |
| 754 | # add contact to admin course as student |
765 | # add contact to admin course as student |
| 755 | # or if this contact and course already exist in a dropped status |
766 | # or if this contact and course already exist in a dropped status |
| 756 | # change the student's status to enrolled |
767 | # change the student's status to enrolled |
| 757 | # DBFIXME can use a REPLACE here? |
|
|
| 758 | if (my $oldUser = $db->getUser($composite_id) ) { |
768 | if (my $oldUser = $db->getUser($composite_id) ) { |
| 759 | warn "Replacing old data for $composite_id status: ". $oldUser->status; |
769 | warn "Replacing old data for $composite_id status: ". $oldUser->status; |
| 760 | $db->deleteUser($composite_id); |
770 | $db->deleteUser($composite_id); |
| 761 | } |
771 | } |
| 762 | eval { $db->addUser($User) }; warn $@ if $@; |
772 | eval { $db->addUser($User) }; warn $@ if $@; |
| … | |
… | |
| 1086 | CGI::tt(CGI::escapeHTML($error)), |
1096 | CGI::tt(CGI::escapeHTML($error)), |
| 1087 | ); |
1097 | ); |
| 1088 | } else { |
1098 | } else { |
| 1089 | # mark the contact person in the admin course as dropped. |
1099 | # mark the contact person in the admin course as dropped. |
| 1090 | # find the contact person for the course by searching the admin classlist. |
1100 | # find the contact person for the course by searching the admin classlist. |
| 1091 | # DBFIXME use a where clause, iterator |
|
|
| 1092 | my @contacts = grep /_$delete_courseID$/, $db->listUsers; |
1101 | my @contacts = grep /_$delete_courseID$/, $db->listUsers; |
| 1093 | if (@contacts) { |
1102 | if (@contacts) { |
| 1094 | die "Incorrect number of contacts for the course $delete_courseID". join(" ", @contacts) if @contacts !=1; |
1103 | die "Incorrect number of contacts for the course $delete_courseID". join(" ", @contacts) if @contacts !=1; |
| 1095 | #warn "contacts", join(" ", @contacts); |
1104 | #warn "contacts", join(" ", @contacts); |
| 1096 | #my $composite_id = "${add_initial_userID}_${add_courseID}"; |
1105 | #my $composite_id = "${add_initial_userID}_${add_courseID}"; |
| … | |
… | |
| 1278 | ); |
1287 | ); |
| 1279 | }; |
1288 | }; |
| 1280 | |
1289 | |
| 1281 | $outputFileHandle->close(); |
1290 | $outputFileHandle->close(); |
| 1282 | |
1291 | |
| 1283 | my $gzip_cmd = "2>&1 ".$ce->{externalPrograms}{gzip}." ".shell_quote($exportFilePath); |
1292 | my $gzipMessage = system( 'gzip', $exportFilePath); |
| 1284 | my $gzip_out = readpipe $gzip_cmd; |
1293 | if ( !$gzipMessage ) { |
| 1285 | if ($?) { |
1294 | $self->addgoodmessage(CGI::p( "Database saved to templates/$exportFileName.gzip. |
| 1286 | my $exit = $? >> 8; |
1295 | You may download it with the file manager.")); |
| 1287 | my $signal = $? & 127; |
|
|
| 1288 | my $core = $? & 128; |
|
|
| 1289 | $self->addbadmessage(CGI::p("Failed to gzip file $exportFilePath with command '$gzip_cmd' (exit=$exit signal=$signal core=$core): $gzip_out")); |
|
|
| 1290 | } else { |
1296 | } else { |
| 1291 | $self->addgoodmessage(CGI::p("Database saved to templates/$exportFileName.gzip. You may download it with the file manager.")); |
1297 | $self->addbadmessage(CGI::p( "Failed to gzip file $exportFilePath")); |
| 1292 | } |
1298 | } |
| 1293 | unlink $exportFilePath; |
1299 | unlink $exportFilePath; |
| 1294 | } # end export of one course |
1300 | } # end export of one course |
| 1295 | #push @errors, "Fatal exception: $@" if $@; |
1301 | #push @errors, "Fatal exception: $@" if $@; |
| 1296 | # |
1302 | # |
| … | |
… | |
| 1483 | |
1489 | |
| 1484 | # locate file |
1490 | # locate file |
| 1485 | my $templateDir = $ce->{courseDirs}->{templates}; |
1491 | my $templateDir = $ce->{courseDirs}->{templates}; |
| 1486 | my $filePath = "$templateDir/$import_file"; |
1492 | my $filePath = "$templateDir/$import_file"; |
| 1487 | |
1493 | |
|
|
1494 | my $gunzipMessage = system( 'gunzip', $filePath); |
|
|
1495 | #FIXME |
|
|
1496 | #warn "gunzip ", $gunzipMessage; |
|
|
1497 | $filePath =~ s/\.gz$//; |
|
|
1498 | #warn "new file path is $filePath"; |
|
|
1499 | my $fileHandle = new IO::File("<$filePath"); |
|
|
1500 | # retrieve upload from upload cache |
| 1488 | # my ($id, $hash) = split /\s+/, $import_file; |
1501 | # my ($id, $hash) = split /\s+/, $import_file; |
| 1489 | # my $upload = WeBWorK::Upload->retrieve($id, $hash, |
1502 | # my $upload = WeBWorK::Upload->retrieve($id, $hash, |
| 1490 | # dir => $ce->{webworkDirs}->{uploadCache} |
1503 | # dir => $ce->{webworkDirs}->{uploadCache} |
| 1491 | # ); |
1504 | # ); |
| 1492 | |
1505 | |
| 1493 | my @errors; |
1506 | my @errors; |
| 1494 | |
1507 | |
| 1495 | my $gzip_cmd = "2>&1 ".$ce->{externalPrograms}{gzip}." -d ".shell_quote($filePath); |
|
|
| 1496 | my $gzip_out = readpipe $gzip_cmd; |
|
|
| 1497 | if ($?) { |
|
|
| 1498 | my $exit = $? >> 8; |
|
|
| 1499 | my $signal = $? & 127; |
|
|
| 1500 | my $core = $? & 128; |
|
|
| 1501 | push @errors, "Failed to ungzip file $filePath with command '$gzip_cmd' (exit=$exit signal=$signal core=$core): $gzip_out"; |
|
|
| 1502 | } else { |
|
|
| 1503 | $filePath =~ s/\.gz$//; |
|
|
| 1504 | my $fileHandle = new IO::File("<$filePath"); |
|
|
| 1505 | eval { |
1508 | eval { |
| 1506 | @errors = dbImport( |
1509 | @errors = dbImport( |
| 1507 | db => $db2, |
1510 | db => $db2, |
| 1508 | # xml => $upload->fileHandle, |
1511 | # xml => $upload->fileHandle, |
| 1509 | xml => $fileHandle, |
1512 | xml => $fileHandle, |
| 1510 | tables => \@import_tables, |
1513 | tables => \@import_tables, |
| 1511 | conflict => $import_conflict, |
1514 | conflict => $import_conflict, |
| 1512 | ); |
1515 | ); |
| 1513 | }; |
1516 | }; |
|
|
1517 | |
| 1514 | push @errors, "Fatal exception: $@" if $@; |
1518 | push @errors, "Fatal exception: $@" if $@; |
| 1515 | } |
1519 | push @errors, $gunzipMessage if $gunzipMessage; |
| 1516 | |
1520 | |
| 1517 | if (@errors) { |
1521 | if (@errors) { |
| 1518 | print CGI::div({class=>"ResultsWithError"}, |
1522 | print CGI::div({class=>"ResultsWithError"}, |
| 1519 | CGI::p("An error occured while importing the database of course $import_courseID:"), |
1523 | CGI::p("An error occured while importing the database of course $import_courseID:"), |
| 1520 | CGI::ul(CGI::li(\@errors)), |
1524 | CGI::ul(CGI::li(\@errors)), |
| … | |
… | |
| 1523 | print CGI::div({class=>"ResultsWithoutError"}, |
1527 | print CGI::div({class=>"ResultsWithoutError"}, |
| 1524 | CGI::p("Import succeeded."), |
1528 | CGI::p("Import succeeded."), |
| 1525 | ); |
1529 | ); |
| 1526 | } |
1530 | } |
| 1527 | } |
1531 | } |
| 1528 | |
|
|
| 1529 | ########################################################################## |
1532 | ########################################################################## |
| 1530 | |
|
|
| 1531 | sub archive_course_form { |
1533 | sub archive_course_form { |
| 1532 | my ($self) = @_; |
1534 | my ($self) = @_; |
| 1533 | my $r = $self->r; |
1535 | my $r = $self->r; |
| 1534 | my $ce = $r->ce; |
1536 | my $ce = $r->ce; |
| 1535 | #my $db = $r->db; |
1537 | #my $db = $r->db; |
| … | |
… | |
| 1734 | CGI::tt(CGI::escapeHTML($error)), |
1736 | CGI::tt(CGI::escapeHTML($error)), |
| 1735 | ); |
1737 | ); |
| 1736 | } else { |
1738 | } else { |
| 1737 | # mark the contact person in the admin course as dropped. |
1739 | # mark the contact person in the admin course as dropped. |
| 1738 | # find the contact person for the course by searching the admin classlist. |
1740 | # find the contact person for the course by searching the admin classlist. |
| 1739 | # DBFIXME where clause, iterator |
|
|
| 1740 | my @contacts = grep /_$archive_courseID$/, $db->listUsers; |
1741 | my @contacts = grep /_$archive_courseID$/, $db->listUsers; |
| 1741 | if (@contacts) { |
1742 | if (@contacts) { |
| 1742 | die "Incorrect number of contacts for the course $archive_courseID". join(" ", @contacts) if @contacts !=1; |
1743 | die "Incorrect number of contacts for the course $archive_courseID". join(" ", @contacts) if @contacts !=1; |
| 1743 | #warn "contacts", join(" ", @contacts); |
1744 | #warn "contacts", join(" ", @contacts); |
| 1744 | #my $composite_id = "${add_initial_userID}_${add_courseID}"; |
1745 | #my $composite_id = "${add_initial_userID}_${add_courseID}"; |
| … | |
… | |
| 1931 | ); |
1932 | ); |
| 1932 | } |
1933 | } |
| 1933 | } |
1934 | } |
| 1934 | |
1935 | |
| 1935 | ################################################################################ |
1936 | ################################################################################ |
|
|
1937 | ## location management routines; added by DG [Danny Ginn] 20070215 |
|
|
1938 | ## revised by glarose |
|
|
1939 | |
|
|
1940 | sub manage_location_form { |
|
|
1941 | my ($self) = @_; |
|
|
1942 | my $r = $self->r; |
|
|
1943 | my $ce = $r->ce; |
|
|
1944 | my $db = $r->db; |
|
|
1945 | #my $authz = $r->authz; |
|
|
1946 | my $urlpath = $r->urlpath; |
|
|
1947 | |
|
|
1948 | # get a list of all existing locations |
|
|
1949 | my @locations = sort {lc($a->location_id) cmp lc($b->location_id)} |
|
|
1950 | $db->getAllLocations(); |
|
|
1951 | my %locAddr = map {$_->location_id => [ $db->listLocationAddresses($_->location_id) ]} @locations; |
|
|
1952 | |
|
|
1953 | my @locationIDs = map { $_->location_id } @locations; |
|
|
1954 | |
|
|
1955 | print CGI::h2("Manage Locations"); |
|
|
1956 | |
|
|
1957 | print CGI::p({},CGI::strong("Currently defined locations are listed below.")); |
|
|
1958 | |
|
|
1959 | print CGI::start_form(-method=>"POST", -action=>$r->uri); |
|
|
1960 | print $self->hidden_authen_fields; |
|
|
1961 | print $self->hidden_fields("subDisplay"); |
|
|
1962 | |
|
|
1963 | # get a list of radio buttons to select an action |
|
|
1964 | my @actionRadios = |
|
|
1965 | CGI::radio_group(-name => "manage_location_action", |
|
|
1966 | -values => ["edit_location_form", |
|
|
1967 | "add_location_handler", |
|
|
1968 | "delete_location_handler"], |
|
|
1969 | -labels => { edit_location_form => "", |
|
|
1970 | add_location_handler => "", |
|
|
1971 | delete_location_handler => "", }, |
|
|
1972 | -default => $r->param("manage_location_action") ? $r->param("manage_location_action") : 'none'); |
|
|
1973 | |
|
|
1974 | print CGI::start_table({}); |
|
|
1975 | print CGI::Tr({}, CGI::th({-colspan=>4,-align=>"left"}, |
|
|
1976 | "Select an action to perform:")); |
|
|
1977 | |
|
|
1978 | # edit action |
|
|
1979 | print CGI::Tr({}, |
|
|
1980 | CGI::td({},[ $actionRadios[0], "Edit Location:" ]), |
|
|
1981 | CGI::td({-colspan=>2, -align=>"left"}, |
|
|
1982 | CGI::div({-style=>"width:25%;"}, |
|
|
1983 | CGI::popup_menu(-name=>"edit_location", |
|
|
1984 | -values=>[@locationIDs]))) ); |
|
|
1985 | # create action |
|
|
1986 | print CGI::Tr({}, |
|
|
1987 | CGI::td({-align=>"left"},[ $actionRadios[1], |
|
|
1988 | "Create Location:" ]), |
|
|
1989 | CGI::td({-colspan=>2}, |
|
|
1990 | "Location name: " . |
|
|
1991 | CGI::textfield(-name=>"new_location_name", |
|
|
1992 | -size=>"10", |
|
|
1993 | -default=>$r->param("new_location_name")?$r->param("new_location_name"):''))); |
|
|
1994 | print CGI::Tr({valign=>'top'}, |
|
|
1995 | CGI::td({}, [" ", "Location description:"]), |
|
|
1996 | CGI::td({-colspan=>2}, |
|
|
1997 | CGI::textfield(-name=>"new_location_description", |
|
|
1998 | -size=>"50", |
|
|
1999 | -default=>$r->param("new_location_description")?$r->param("new_location_description"):'')) ); |
|
|
2000 | print CGI::Tr({}, CGI::td({}," "), |
|
|
2001 | CGI::td({-colspan=>3}, "Addresses for new location " . |
|
|
2002 | "(enter one per line, as single IP addresses " . |
|
|
2003 | "(e.g., 192.168.1.101), address masks (e.g., " . |
|
|
2004 | "192.168.1.0/24), or IP ranges (e.g., " . |
|
|
2005 | "192.168.1.101-192.168.1.150)):")); |
|
|
2006 | print CGI::Tr({}, CGI::td({}, " "), |
|
|
2007 | CGI::td({-colspan=>3}, |
|
|
2008 | CGI::textarea({-name=>"new_location_addresses", |
|
|
2009 | -rows=>5, -columns=>28, |
|
|
2010 | -default=>$r->param("new_location_addresses")?$r->param("new_location_addresses"):''}))); |
|
|
2011 | |
|
|
2012 | # delete action |
|
|
2013 | print CGI::Tr({}, |
|
|
2014 | CGI::td({-colspan=>4}, |
|
|
2015 | CGI::div({-class=>"ResultsWithError"}, |
|
|
2016 | CGI::em({}, "Deletion deletes all location " . |
|
|
2017 | "data and related addresses, and is" . |
|
|
2018 | |
|
|
2019 | " not undoable!")))); |
|
|
2020 | print CGI::Tr({}, |
|
|
2021 | CGI::td({}, |
|
|
2022 | [ $actionRadios[2], |
|
|
2023 | CGI::div({-class=>"ResultsWithError"}, |
|
|
2024 | "Delete location:") ]), |
|
|
2025 | CGI::td({-colspan=>2}, |
|
|
2026 | CGI::popup_menu(-name=>"delete_location", |
|
|
2027 | -values=>["no location", |
|
|
2028 | @locationIDs]) . |
|
|
2029 | CGI::span({-style=>"color:#C33;"}, " Confirm: ") . |
|
|
2030 | CGI::checkbox({-name=>"delete_confirm", |
|
|
2031 | -value=>"true", |
|
|
2032 | -label=>""}) ) ); |
|
|
2033 | print CGI::end_table(); |
|
|
2034 | |
|
|
2035 | print CGI::p({}, CGI::submit(-name=>"manage_locations", -value=>"Take Action!")); |
|
|
2036 | |
|
|
2037 | print CGI::end_form(); |
|
|
2038 | |
|
|
2039 | # existing location table |
|
|
2040 | # FIXME: the styles for this table should be off in a stylesheet |
|
|
2041 | # somewhere |
|
|
2042 | print CGI::start_div({align=>"center"}), |
|
|
2043 | CGI::start_table({border=>1, cellpadding=>2}); |
|
|
2044 | print CGI::Tr({style=>"background-color:#e0e0e0;font-size:92%", align=>"left"}, |
|
|
2045 | CGI::th({}, ["Location","Description","Addresses"])); |
|
|
2046 | foreach my $loc ( @locations ) { |
|
|
2047 | print CGI::Tr({valign=>'top',style=>"background-color:#eeeeee;"}, |
|
|
2048 | CGI::td({style=>'font-size:85%;'}, |
|
|
2049 | [ $loc->location_id, |
|
|
2050 | $loc->description, |
|
|
2051 | join(', ', @{$locAddr{$loc->location_id}}) ])); |
|
|
2052 | } |
|
|
2053 | print CGI::end_table(), CGI::end_div(); |
|
|
2054 | |
|
|
2055 | } |
|
|
2056 | |
|
|
2057 | sub add_location_handler { |
|
|
2058 | my $self = shift(); |
|
|
2059 | my $r = $self->r; |
|
|
2060 | my $db = $r->db; |
|
|
2061 | |
|
|
2062 | # the location data we're to add |
|
|
2063 | my $locationID = $r->param("new_location_name"); |
|
|
2064 | my $locationDescr = $r->param("new_location_description"); |
|
|
2065 | my $locationAddr = $r->param("new_location_addresses"); |
|
|
2066 | # break the addresses up |
|
|
2067 | $locationAddr =~ s/\s*-\s*/-/g; |
|
|
2068 | $locationAddr =~ s/\s*\/\s*/\//g; |
|
|
2069 | my @addresses = split(/\s+/, $locationAddr); |
|
|
2070 | |
|
|
2071 | # sanity checks |
|
|
2072 | my $badAddr = ''; |
|
|
2073 | foreach my $addr ( @addresses ) { |
|
|
2074 | unless ( new Net::IP($addr) ) { |
|
|
2075 | $badAddr .= "$addr, "; |
|
|
2076 | $locationAddr =~ s/$addr\n//s; |
|
|
2077 | } |
|
|
2078 | } |
|
|
2079 | $badAddr =~ s/, $//; |
|
|
2080 | |
|
|
2081 | # a check to be sure that the location addresses don't already |
|
|
2082 | # exist |
|
|
2083 | my $badLocAddr; |
|
|
2084 | if ( ! $badAddr && $locationID ) { |
|
|
2085 | if ( $db->countLocationAddresses( $locationID ) ) { |
|
|
2086 | my @allLocAddr = $db->listLocationAddresses($locationID); |
|
|
2087 | foreach my $addr ( @addresses ) { |
|
|
2088 | $badLocAddr .= "$addr, " |
|
|
2089 | if ( grep {/^$addr$/} @allLocAddr ); |
|
|
2090 | } |
|
|
2091 | $badLocAddr =~ s/, $//; |
|
|
2092 | } |
|
|
2093 | } |
|
|
2094 | |
|
|
2095 | if ( ! @addresses || ! $locationID || ! $locationDescr ) { |
|
|
2096 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2097 | "Missing required input data. Please check " . |
|
|
2098 | "that you have filled in all of the create " . |
|
|
2099 | "location fields and resubmit."); |
|
|
2100 | } elsif ( $badAddr ) { |
|
|
2101 | $r->param("new_location_addresses", $locationAddr); |
|
|
2102 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2103 | "Address(es) $badAddr is(are) not in a " . |
|
|
2104 | "recognized form. Please check your " . |
|
|
2105 | "data entry and resubmit."); |
|
|
2106 | } elsif ( $db->existsLocation( $locationID ) ) { |
|
|
2107 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2108 | "A location with the name $locationID " . |
|
|
2109 | "already exists in the database. Did " . |
|
|
2110 | "you mean to edit that location instead?"); |
|
|
2111 | } elsif ( $badLocAddr ) { |
|
|
2112 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2113 | "Address(es) $badLocAddr already exist " . |
|
|
2114 | "in the database. THIS SHOULD NOT HAPPEN! " . |
|
|
2115 | "Please double check the integrity of " . |
|
|
2116 | "the WeBWorK database before continuing."); |
|
|
2117 | } else { |
|
|
2118 | # add the location |
|
|
2119 | my $locationObj = $db->newLocation; |
|
|
2120 | $locationObj->location_id( $locationID ); |
|
|
2121 | $locationObj->description( $locationDescr ); |
|
|
2122 | $db->addLocation( $locationObj ); |
|
|
2123 | |
|
|
2124 | # and add the addresses |
|
|
2125 | foreach my $addr ( @addresses ) { |
|
|
2126 | my $locationAddress = $db->newLocationAddress; |
|
|
2127 | $locationAddress->location_id($locationID); |
|
|
2128 | $locationAddress->ip_mask($addr); |
|
|
2129 | |
|
|
2130 | $db->addLocationAddress( $locationAddress ); |
|
|
2131 | } |
|
|
2132 | |
|
|
2133 | # we've added the location, so clear those param |
|
|
2134 | # entries |
|
|
2135 | $r->param('manage_location_action','none'); |
|
|
2136 | $r->param('new_location_name',''); |
|
|
2137 | $r->param('new_location_description',''); |
|
|
2138 | $r->param('new_location_addresses',''); |
|
|
2139 | |
|
|
2140 | print CGI::div({-class=>"ResultsWithoutError"}, |
|
|
2141 | "Location $locationID has been created, " . |
|
|
2142 | "with addresses " . join(', ', @addresses) . |
|
|
2143 | "."); |
|
|
2144 | } |
|
|
2145 | |
|
|
2146 | $self->manage_location_form; |
|
|
2147 | } |
|
|
2148 | |
|
|
2149 | sub delete_location_handler { |
|
|
2150 | my $self = shift; |
|
|
2151 | my $r = $self->r; |
|
|
2152 | my $db = $r->db; |
|
|
2153 | |
|
|
2154 | # what location are we deleting? |
|
|
2155 | my $locationID = $r->param("delete_location"); |
|
|
2156 | # are we sure? |
|
|
2157 | my $confirm = $r->param("delete_confirm"); |
|
|
2158 | |
|
|
2159 | if ( ! $locationID ) { |
|
|
2160 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2161 | "Please provide a location name " . |
|
|
2162 | "to delete."); |
|
|
2163 | |
|
|
2164 | } elsif ( ! $db->existsLocation($locationID) ) { |
|
|
2165 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2166 | "No location with name $locationID " . |
|
|
2167 | "exists in the database."); |
|
|
2168 | |
|
|
2169 | } elsif ( ! $confirm || $confirm ne 'true' ) { |
|
|
2170 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2171 | "Location deletion requires confirmation."); |
|
|
2172 | } else { |
|
|
2173 | $db->deleteLocation( $locationID ); |
|
|
2174 | print CGI::div({-class=>"ResultsWithoutError"}, |
|
|
2175 | "Location $locationID has been deleted."); |
|
|
2176 | $r->param('manage_location_action','none'); |
|
|
2177 | $r->param('delete_location',''); |
|
|
2178 | } |
|
|
2179 | $self->manage_location_form; |
|
|
2180 | } |
|
|
2181 | |
|
|
2182 | sub edit_location_form { |
|
|
2183 | my $self = shift; |
|
|
2184 | my $r = $self->r; |
|
|
2185 | my $db = $r->db; |
|
|
2186 | |
|
|
2187 | my $locationID = $r->param("edit_location"); |
|
|
2188 | if ( $db->existsLocation( $locationID ) ) { |
|
|
2189 | my $location = $db->getLocation($locationID); |
|
|
2190 | # this doesn't give that nice a sort for IP addresses, |
|
|
2191 | # b/c there's the problem with 192.168.1.168 sorting |
|
|
2192 | # ahead of 192.168.1.2. we could do better if we |
|
|
2193 | # either invoked Net::IP in the sort routine, or if |
|
|
2194 | # we insisted on dealing only with IPv4. rather than |
|
|
2195 | # deal with either of those, we'll leave this for now |
|
|
2196 | my @locAddresses = sort { $a cmp $b } |
|
|
2197 | $db->listLocationAddresses($locationID); |
|
|
2198 | |
|
|
2199 | print CGI::h2("Editing location ", $locationID); |
|
|
2200 | |
|
|
2201 | print CGI::p({},"Edit the current value of the location ", |
|
|
2202 | "description, if desired, then add and select ", |
|
|
2203 | "addresses to delete, and then click the ", |
|
|
2204 | "\"Take Action\" button to make all of your ", |
|
|
2205 | "changes. Or, click \"Manage Locations\" ", |
|
|
2206 | "above to make no changes and return to the ", |
|
|
2207 | "Manage Locations page."); |
|
|
2208 | |
|
|
2209 | print CGI::start_form(-method=>"POST", |
|
|
2210 | -action=>$r->uri); |
|
|
2211 | print $self->hidden_authen_fields; |
|
|
2212 | print $self->hidden_fields("subDisplay"); |
|
|
2213 | print CGI::hidden(-name=>'edit_location', |
|
|
2214 | -default=>$locationID); |
|
|
2215 | print CGI::hidden(-name=>'manage_location_action', |
|
|
2216 | -default=>'edit_location_handler'); |
|
|
2217 | |
|
|
2218 | print CGI::start_table(); |
|
|
2219 | print CGI::Tr({-valign=>'top'}, |
|
|
2220 | CGI::td({-colspan=>3}, |
|
|
2221 | "Location description: ", CGI::br(), |
|
|
2222 | CGI::textfield(-name=>"location_description", |
|
|
2223 | -size=>"50", |
|
|
2224 | -default=>$location->description))); |
|
|
2225 | print CGI::Tr({-valign=>'top'}, |
|
|
2226 | CGI::td({-width=>"50%"}, |
|
|
2227 | "Addresses to add to the location " . |
|
|
2228 | "(enter one per line, as single IP addresses " . |
|
|
2229 | "(e.g., 192.168.1.101), address masks " . |
|
|
2230 | "(e.g., 192.168.1.0/24), or IP ranges " . |
|
|
2231 | "(e.g., 192.168.1.101-192.168.1.150)):" . |
|
|
2232 | CGI::br() . |
|
|
2233 | CGI::textarea({-name=>"new_location_addresses", |
|
|
2234 | -rows=>5, -columns=>28})), |
|
|
2235 | CGI::td({}, " "), |
|
|
2236 | CGI::td({-width=>"50%"}, |
|
|
2237 | "Existing addresses for the location are " . |
|
|
2238 | "given in the scrolling list below. Select " . |
|
|
2239 | "addresses from the list to delete them:" . |
|
|
2240 | CGI::br() . |
|
|
2241 | CGI::scrolling_list(-name=>'delete_location_addresses', |
|
|
2242 | -values=>[@locAddresses], |
|
|
2243 | -size=>8, |
|
|
2244 | -multiple=>'multiple') . |
|
|
2245 | CGI::br() . "or: " . |
|
|
2246 | CGI::checkbox(-name=>'delete_all_addresses', |
|
|
2247 | -value=>'true', |
|
|
2248 | -label=>'Delete all existing addresses') |
|
|
2249 | )); |
|
|
2250 | |
|
|
2251 | print CGI::end_table(); |
|
|
2252 | |
|
|
2253 | print CGI::p({},CGI::submit(-value=>'Take Action!')); |
|
|
2254 | |
|
|
2255 | } else { |
|
|
2256 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2257 | "Location $locationID does not exist " . |
|
|
2258 | "in the WeBWorK database. Please check " . |
|
|
2259 | "your input (perhaps you need to reload " . |
|
|
2260 | "the location management page?)."); |
|
|
2261 | |
|
|
2262 | $self->manage_location_form; |
|
|
2263 | } |
|
|
2264 | } |
|
|
2265 | |
|
|
2266 | sub edit_location_handler { |
|
|
2267 | my $self = shift; |
|
|
2268 | my $r = $self->r; |
|
|
2269 | my $db = $r->db; |
|
|
2270 | |
|
|
2271 | my $locationID = $r->param("edit_location"); |
|
|
2272 | my $locationDesc = $r->param("location_description"); |
|
|
2273 | my $addAddresses = $r->param("new_location_addresses"); |
|
|
2274 | my @delAddresses = $r->param("delete_location_addresses"); |
|
|
2275 | my $deleteAll = $r->param("delete_all_addresses"); |
|
|
2276 | |
|
|
2277 | # gut check |
|
|
2278 | if ( ! $locationID ) { |
|
|
2279 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2280 | "No location specified to edit?! " . |
|
|
2281 | "Please check your input data."); |
|
|
2282 | $self->manage_location_form; |
|
|
2283 | |
|
|
2284 | } elsif ( ! $db->existsLocation( $locationID ) ) { |
|
|
2285 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2286 | "Location $locationID does not exist " . |
|
|
2287 | "in the WeBWorK database. Please check " . |
|
|
2288 | "your input (perhaps you need to reload " . |
|
|
2289 | "the location management page?)."); |
|
|
2290 | $self->manage_location_form; |
|
|
2291 | } else { |
|
|
2292 | my $location = $db->getLocation($locationID); |
|
|
2293 | |
|
|
2294 | # get the current location addresses. if we're deleting |
|
|
2295 | # all of the existing addresses, we don't use this list |
|
|
2296 | # to determine which addresses to add, however. |
|
|
2297 | my @currentAddr = $db->listLocationAddresses($locationID); |
|
|
2298 | my @compareAddr = ( ! $deleteAll || $deleteAll ne 'true' ) |
|
|
2299 | ? @currentAddr : (); |
|
|
2300 | |
|
|
2301 | my $doneMsg = ''; |
|
|
2302 | |
|
|
2303 | if ($locationDesc && $location->description ne $locationDesc) { |
|
|
2304 | $location->description($locationDesc); |
|
|
2305 | $db->putLocation($location); |
|
|
2306 | $doneMsg .= CGI::p({},"Updated location description."); |
|
|
2307 | } |
|
|
2308 | # get the actual addresses to add out of the text field |
|
|
2309 | $addAddresses =~ s/\s*-\s*/-/g; |
|
|
2310 | $addAddresses =~ s/\s*\/\s*/\//g; |
|
|
2311 | my @addAddresses = split(/\s+/, $addAddresses); |
|
|
2312 | |
|
|
2313 | # make sure that we're adding and deleting only those |
|
|
2314 | # addresses that are not yet/currently in the location |
|
|
2315 | # addresses |
|
|
2316 | my @toAdd = (); my @noAdd = (); |
|
|
2317 | my @toDel = (); my @noDel = (); |
|
|
2318 | foreach my $addr ( @addAddresses ) { |
|
|
2319 | if (grep {/^$addr$/} @compareAddr) {push(@noAdd,$addr);} |
|
|
2320 | else { push(@toAdd, $addr); } |
|
|
2321 | } |
|
|
2322 | if ( $deleteAll && $deleteAll eq 'true' ) { |
|
|
2323 | @toDel = @currentAddr; |
|
|
2324 | } else { |
|
|
2325 | foreach my $addr ( @delAddresses ) { |
|
|
2326 | if (grep {/^$addr$/} @currentAddr) { |
|
|
2327 | push(@toDel,$addr); |
|
|
2328 | } else { push(@noDel, $addr); } |
|
|
2329 | } |
|
|
2330 | } |
|
|
2331 | |
|
|
2332 | # and make sure that all of the addresses we're adding are |
|
|
2333 | # a sensible form |
|
|
2334 | my $badAddr = ''; |
|
|
2335 | foreach my $addr ( @toAdd ) { |
|
|
2336 | unless ( new Net::IP($addr) ) { |
|
|
2337 | $badAddr .= "$addr, "; |
|
|
2338 | } |
|
|
2339 | } |
|
|
2340 | $badAddr =~ s/, $//; |
|
|
2341 | |
|
|
2342 | # delete addresses first, because we allow deletion of |
|
|
2343 | # all existing addresses, then addition of addresses. |
|
|
2344 | # note that we don't allow deletion and then addition |
|
|
2345 | # of the same address normally, however; in that case |
|
|
2346 | # we'll end up just deleting the address. |
|
|
2347 | foreach ( @toDel ) { |
|
|
2348 | $db->deleteLocationAddress($locationID, $_); |
|
|
2349 | } |
|
|
2350 | foreach ( @toAdd ) { |
|
|
2351 | my $locAddr = $db->newLocationAddress; |
|
|
2352 | $locAddr->location_id($locationID); |
|
|
2353 | $locAddr->ip_mask($_); |
|
|
2354 | |
|
|
2355 | $db->addLocationAddress($locAddr); |
|
|
2356 | } |
|
|
2357 | |
|
|
2358 | my $addrMsg = ''; |
|
|
2359 | $addrMsg .= "Deleted addresses " . join(', ', @toDel) . |
|
|
2360 | " from location." . CGI::br() if ( @toDel ); |
|
|
2361 | $addrMsg .= "Added addresses " . join(', ', @toAdd) . |
|
|
2362 | " to location $locationID. " if ( @toAdd ); |
|
|
2363 | |
|
|
2364 | my $badMsg = ''; |
|
|
2365 | $badMsg .= 'Address(es) ' . join(', ', @noAdd) . |
|
|
2366 | " in the add list is(are) already in the " . |
|
|
2367 | "location $locationID, and so were " . |
|
|
2368 | "skipped." . CGI::br() if ( @noAdd ); |
|
|
2369 | $badMsg .= "Address(es) $badAddr is(are) not in a " . |
|
|
2370 | "recognized form. Please check your data " . |
|
|
2371 | "entry and try again." . CGI::br() if ( $badAddr ); |
|
|
2372 | $badMsg .= 'Address(es) ' . join(', ', @noDel) . |
|
|
2373 | " in the delete list is(are) not in the " . |
|
|
2374 | "location $locationID, and so were " . |
|
|
2375 | "skipped." if ( @noDel ); |
|
|
2376 | |
|
|
2377 | print CGI::div({-class=>"ResultsWithError"}, $badMsg) |
|
|
2378 | if ( $badMsg ); |
|
|
2379 | if ( $doneMsg || $addrMsg ) { |
|
|
2380 | print CGI::div({-class=>"ResultsWithoutError"}, |
|
|
2381 | CGI::p({}, $doneMsg, $addrMsg)); |
|
|
2382 | } else { |
|
|
2383 | print CGI::div({-class=>"ResultsWithError"}, |
|
|
2384 | "No valid changes submitted for ", |
|
|
2385 | "location $locationID."); |
|
|
2386 | } |
|
|
2387 | |
|
|
2388 | $self->edit_location_form; |
|
|
2389 | } |
|
|
2390 | } |
|
|
2391 | |
|
|
2392 | ################################################################################ |
| 1936 | 1; |
2393 | 1; |