[system] / branches / rel-2-4-patches / webwork2 / lib / WeBWorK / ContentGenerator / CourseAdmin.pm Repository:
ViewVC logotype

Diff of /branches/rel-2-4-patches/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm

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

Revision 3621 Revision 4918
1################################################################################ 1################################################################################
2# WeBWorK Online Homework Delivery System 2# WeBWorK Online Homework Delivery System
3# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ 3# Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/
4# $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v 1.40 2005/08/14 16:51:15 gage Exp $ 4# $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v 1.63 2007/03/30 14:21:14 glarose 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.
23 23
24=cut 24=cut
25 25
26use strict; 26use strict;
27use warnings; 27use warnings;
28use CGI::Pretty qw(); 28#use CGI qw(-nosticky );
29use WeBWorK::CGI;
29use Data::Dumper; 30use Data::Dumper;
30use File::Temp qw/tempfile/; 31use File::Temp qw/tempfile/;
31use WeBWorK::CourseEnvironment; 32use WeBWorK::CourseEnvironment;
32use IO::File; 33use IO::File;
34use WeBWorK::Debug;
33use WeBWorK::Utils qw(cryptPassword writeLog listFilesRecursive); 35use WeBWorK::Utils qw(cryptPassword writeLog listFilesRecursive);
34use WeBWorK::Utils::CourseManagement qw(addCourse renameCourse deleteCourse listCourses archiveCourse); 36use WeBWorK::Utils::CourseManagement qw(addCourse renameCourse deleteCourse listCourses archiveCourse
37 listArchivedCourses unarchiveCourse);
35use WeBWorK::Utils::DBImportExport qw(dbExport dbImport); 38use WeBWorK::Utils::DBImportExport qw(dbExport dbImport);
39# needed for location management
40use Net::IP;
36 41
37# put the following database layouts at the top of the list, in this order 42use constant IMPORT_EXPORT_WARNING => "The ability to import and export
38our @DB_LAYOUT_ORDER = qw/sql_single gdbm sql/; 43databases is still under development. It seems to work but it is <b>VERY</b>
39 44slow on large courses. You may prefer to use webwork2/bin/wwdb or the mysql
40our %DB_LAYOUT_DESCRIPTIONS = ( 45dump facility for archiving large courses. Please send bug reports if you find
41 gdbm => CGI::i("Deprecated. Uses GDBM databases to record WeBWorK data. Use this layout if the course must be used with WeBWorK 1.x."), 46errors.";
42 sql => CGI::i("Deprecated. Uses a separate SQL database to record WeBWorK data for each course."),
43 sql_single => "Uses a single SQL database to record WeBWorK data for all courses using this layout. This is the recommended layout for new courses.",
44);
45 47
46sub pre_header_initialize { 48sub pre_header_initialize {
47 my ($self) = @_; 49 my ($self) = @_;
48 my $r = $self->r; 50 my $r = $self->r;
49 my $ce = $r->ce; 51 my $ce = $r->ce;
185 } else { 187 } else {
186 # form only 188 # form only
187 $method_to_call = "archive_course_form"; 189 $method_to_call = "archive_course_form";
188 } 190 }
189 } 191 }
192 elsif ($subDisplay eq "unarchive_course") {
193 if (defined $r->param("unarchive_course")) {
194 # validate or confirm
195 @errors = $self->unarchive_course_validate;
196 if (@errors) {
197 $method_to_call = "unarchive_course_form";
198 } else {
199 $method_to_call = "unarchive_course_confirm";
200 }
201 } elsif (defined $r->param("confirm_unarchive_course")) {
202 # validate and archive
203 @errors = $self->unarchive_course_validate;
204 if (@errors) {
205 $method_to_call = "unarchive_course_form";
206 } else {
207 $method_to_call = "do_unarchive_course";
208 }
209 } else {
210 # form only
211 $method_to_call = "unarchive_course_form";
212 }
190 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 }
191 else { 223 else {
192 @errors = "Unrecognized sub-display @{[ CGI::b($subDisplay) ]}."; 224 @errors = "Unrecognized sub-display @{[ CGI::b($subDisplay) ]}.";
193 } 225 }
194
195 } 226 }
196 227
197 $self->{errors} = \@errors; 228 $self->{errors} = \@errors;
198 $self->{method_to_call} = $method_to_call; 229 $self->{method_to_call} = $method_to_call;
199} 230}
275 CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"export_database"})}, "Export Database"), 306 CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"export_database"})}, "Export Database"),
276 " | ", 307 " | ",
277 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"),
278 " | ", 309 " | ",
279 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"),
311 "|",
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"),
280 CGI::hr(), 315 CGI::hr(),
281 $methodMessage, 316 $methodMessage,
282 317
283 ); 318 );
284
285 print CGI::p("The ability to import and to export databases is still under development.
286 It seems to work but it is <b>VERY</b> slow on large courses. You may prefer to
287 use webwork2/bin/wwdb or the mysql dump facility for archiving large courses.
288 Please send bug reports if you find errors. ");
289 319
290 my @errors = @{$self->{errors}}; 320 my @errors = @{$self->{errors}};
291 321
292 322
293 if (@errors) { 323 if (@errors) {
324 ); 354 );
325 355
326 } 356 }
327 357
328 print CGI::end_ol(); 358 print CGI::end_ol();
359
360 print CGI::h2("Archived Courses");
361 print CGI::start_ol();
362
363 @courseIDs = listArchivedCourses($ce);
364 foreach my $courseID (sort {lc($a) cmp lc($b) } @courseIDs) {
365 print CGI::li($courseID),
366 }
367
368 print CGI::end_ol();
329 } 369 }
330 return ""; 370 return "";
331} 371}
332 372
333################################################################################ 373################################################################################
354 my $add_initial_email = $r->param("add_initial_email") || ""; 394 my $add_initial_email = $r->param("add_initial_email") || "";
355 395
356 my $add_templates_course = $r->param("add_templates_course") || ""; 396 my $add_templates_course = $r->param("add_templates_course") || "";
357 397
358 my $add_dbLayout = $r->param("add_dbLayout") || ""; 398 my $add_dbLayout = $r->param("add_dbLayout") || "";
359 my $add_sql_host = $r->param("add_sql_host") || "";
360 my $add_sql_port = $r->param("add_sql_port") || "";
361 my $add_sql_username = $r->param("add_sql_username") || "";
362 my $add_sql_password = $r->param("add_sql_password") || "";
363 my $add_sql_database = $r->param("add_sql_database") || "";
364 my $add_sql_wwhost = $r->param("add_sql_wwhost") || "";
365 my $add_gdbm_globalUserID = $r->param("add_gdbm_globalUserID") || "";
366 399
367 my @dbLayouts = do { 400 my @dbLayouts = do {
368 my @ordered_layouts; 401 my @ordered_layouts;
369 foreach my $layout (@DB_LAYOUT_ORDER) { 402 foreach my $layout (@{$ce->{dbLayout_order}}) {
370 if (exists $ce->{dbLayouts}->{$layout}) { 403 if (exists $ce->{dbLayouts}->{$layout}) {
371 push @ordered_layouts, $layout; 404 push @ordered_layouts, $layout;
372 } 405 }
373 } 406 }
374 407
388 $ce->{webworkURLs}->{root}, 421 $ce->{webworkURLs}->{root},
389 $ce->{pg}->{directories}->{root}, 422 $ce->{pg}->{directories}->{root},
390 "COURSENAME", 423 "COURSENAME",
391 ); 424 );
392 425
393 my $dbi_source = do {
394 # find the most common SQL source (stolen from CourseManagement.pm)
395 my %sources;
396 foreach my $table (keys %{ $ce2->{dbLayouts}->{sql} }) {
397 $sources{$ce2->{dbLayouts}->{sql}->{$table}->{source}}++;
398 }
399 my $source;
400 if (keys %sources > 1) {
401 foreach my $curr (keys %sources) {
402 $source = $curr if not defined $source or
403 $sources{$curr} > $sources{$source};
404 }
405 } else {
406 ($source) = keys %sources;
407 }
408 $source;
409 };
410
411 my @existingCourses = listCourses($ce); 426 my @existingCourses = listCourses($ce);
412 @existingCourses = sort { lc($a) cmp lc ($b) } @existingCourses; #make sort case insensitive 427 @existingCourses = sort { lc($a) cmp lc ($b) } @existingCourses; #make sort case insensitive
413 428
414 print CGI::h2("Add Course"); 429 print CGI::h2("Add Course");
415 430
416 print CGI::start_form("POST", $r->uri); 431 print CGI::start_form(-method=>"POST", -action=>$r->uri);
417 print $self->hidden_authen_fields; 432 print $self->hidden_authen_fields;
418 print $self->hidden_fields("subDisplay"); 433 print $self->hidden_fields("subDisplay");
419 434
420 print CGI::p("Specify an ID, title, and institution for the new course. The course ID may contain only letters, numbers, hyphens, and underscores."); 435 print CGI::p("Specify an ID, title, and institution for the new course. The course ID may contain only letters, numbers, hyphens, and underscores.");
421 436
422 print CGI::table({class=>"FormLayout"}, 437 print CGI::table({class=>"FormLayout"},
423 CGI::Tr( 438 CGI::Tr({},
424 CGI::th({class=>"LeftHeader"}, "Course ID:"), 439 CGI::th({class=>"LeftHeader"}, "Course ID:"),
425 CGI::td(CGI::textfield("add_courseID", $add_courseID, 25)), 440 CGI::td(CGI::textfield(-name=>"add_courseID", -value=>$add_courseID, -size=>25)),
426 ), 441 ),
427 CGI::Tr( 442 CGI::Tr({},
428 CGI::th({class=>"LeftHeader"}, "Course Title:"), 443 CGI::th({class=>"LeftHeader"}, "Course Title:"),
429 CGI::td(CGI::textfield("add_courseTitle", $add_courseTitle, 25)), 444 CGI::td(CGI::textfield(-name=>"add_courseTitle", -value=>$add_courseTitle, -size=>25)),
430 ), 445 ),
431 CGI::Tr( 446 CGI::Tr({},
432 CGI::th({class=>"LeftHeader"}, "Institution:"), 447 CGI::th({class=>"LeftHeader"}, "Institution:"),
433 CGI::td(CGI::textfield("add_courseInstitution", $add_courseInstitution, 25)), 448 CGI::td(CGI::textfield(-name=>"add_courseInstitution", -value=>$add_courseInstitution, -size=>25)),
434 ), 449 ),
435 ); 450 );
436 451
437 print CGI::p("To add the WeBWorK administrators to the new course (as instructors) check the box below."); 452 print CGI::p("To add the WeBWorK administrators to the new course (as instructors) check the box below.");
438 my $checked = ($add_admin_users) ?"checked": ""; # workaround because CGI::checkbox seems to have a bug -- it won't default to checked. 453 my @checked = ($add_admin_users) ?(checked=>1): (); # workaround because CGI::checkbox seems to have a bug -- it won't default to checked.
439 print CGI::p(CGI::input({-type=>'checkbox', -name=>"add_admin_users", $checked=>'' }, "Add WeBWorK administrators to new course")); 454 print CGI::p({},CGI::input({-type=>'checkbox', -name=>"add_admin_users", @checked }, "Add WeBWorK administrators to new course"));
440 455
441 print CGI::p("To add an additional instructor to the new course, specify user information below. The user ID may contain only numbers, letters, hyphens, and underscores."); 456 print CGI::p("To add an additional instructor to the new course, specify user information below. The user ID may contain only
457 numbers, letters, hyphens, periods (dots), commas,and underscores.\n");
442 458
443 print CGI::table({class=>"FormLayout"}, CGI::Tr( 459 print CGI::table({class=>"FormLayout"}, CGI::Tr({},
444 CGI::td( 460 CGI::td({},
445 CGI::table({class=>"FormLayout"}, 461 CGI::table({class=>"FormLayout"},
446 CGI::Tr( 462 CGI::Tr({},
447 CGI::th({class=>"LeftHeader"}, "User ID:"), 463 CGI::th({class=>"LeftHeader"}, "User ID:"),
448 CGI::td(CGI::textfield("add_initial_userID", $add_initial_userID, 25)), 464 CGI::td(CGI::textfield(-name=>"add_initial_userID", -value=>$add_initial_userID, -size=>25)),
449 ), 465 ),
450 CGI::Tr( 466 CGI::Tr({},
451 CGI::th({class=>"LeftHeader"}, "Password:"), 467 CGI::th({class=>"LeftHeader"}, "Password:"),
452 CGI::td(CGI::password_field("add_initial_password", $add_initial_password, 25)), 468 CGI::td(CGI::password_field(-name=>"add_initial_password", -value=>$add_initial_password, -size=>25)),
453 ), 469 ),
454 CGI::Tr( 470 CGI::Tr({},
455 CGI::th({class=>"LeftHeader"}, "Confirm Password:"), 471 CGI::th({class=>"LeftHeader"}, "Confirm Password:"),
456 CGI::td(CGI::password_field("add_initial_confirmPassword", $add_initial_confirmPassword, 25)), 472 CGI::td(CGI::password_field(-name=>"add_initial_confirmPassword", -value=>$add_initial_confirmPassword, -size=>25)),
457 ), 473 ),
458 ), 474 ),
459 ), 475 ),
460 CGI::td( 476 CGI::td({},
461 CGI::table({class=>"FormLayout"}, 477 CGI::table({class=>"FormLayout"},
462 CGI::Tr( 478 CGI::Tr({},
463 CGI::th({class=>"LeftHeader"}, "First Name:"), 479 CGI::th({class=>"LeftHeader"}, "First Name:"),
464 CGI::td(CGI::textfield("add_initial_firstName", $add_initial_firstName, 25)), 480 CGI::td(CGI::textfield(-name=>"add_initial_firstName", -value=>$add_initial_firstName, -size=>25)),
465 ), 481 ),
466 CGI::Tr( 482 CGI::Tr({},
467 CGI::th({class=>"LeftHeader"}, "Last Name:"), 483 CGI::th({class=>"LeftHeader"}, "Last Name:"),
468 CGI::td(CGI::textfield("add_initial_lastName", $add_initial_lastName, 25)), 484 CGI::td(CGI::textfield(-name=>"add_initial_lastName", -value=>$add_initial_lastName, -size=>25)),
469 ), 485 ),
470 CGI::Tr( 486 CGI::Tr({},
471 CGI::th({class=>"LeftHeader"}, "Email Address:"), 487 CGI::th({class=>"LeftHeader"}, "Email Address:"),
472 CGI::td(CGI::textfield("add_initial_email", $add_initial_email, 25)), 488 CGI::td(CGI::textfield(-name=>"add_initial_email", -value=>$add_initial_email, -size=>25)),
473 ), 489 ),
474 ), 490 ),
475 491
476 ), 492 ),
477 )); 493 ));
478 494
479 print CGI::p("To copy problem templates from an existing course, select the course below."); 495 print CGI::p("To copy problem templates from an existing course, select the course below.");
480 496
481 print CGI::table({class=>"FormLayout"}, 497 print CGI::table({class=>"FormLayout"},
482 CGI::Tr( 498 CGI::Tr({},
483 CGI::th({class=>"LeftHeader"}, "Copy templates from:"), 499 CGI::th({class=>"LeftHeader"}, "Copy templates from:"),
484 CGI::td( 500 CGI::td(
485 CGI::popup_menu( 501 CGI::popup_menu(
486 -name => "add_templates_course", 502 -name => "add_templates_course",
487 -values => [ "", @existingCourses ], 503 -values => [ "", @existingCourses ],
493 509
494 ), 510 ),
495 ), 511 ),
496 ); 512 );
497 513
514
515
498 print CGI::p("Select a database layout below."); 516 print CGI::p("Select a database layout below.");
517 print CGI::start_table({class=>"FormLayout"});
499 518
519 my %dbLayout_buttons;
520 my $selected_dbLayout = defined $add_dbLayout ? $add_dbLayout : $ce->{dbLayout_order}[0];
521 @dbLayout_buttons{@dbLayouts} = CGI::radio_group(-name=>"add_dbLayout",-values=>\@dbLayouts,-default=>$selected_dbLayout);
500 foreach my $dbLayout (@dbLayouts) { 522 foreach my $dbLayout (@dbLayouts) {
501 print CGI::start_table({class=>"FormLayout"}); 523 my $dbLayoutLabel = (defined $ce->{dbLayout_descr}{$dbLayout})
502 524 ? "$dbLayout - " . $ce->{dbLayout_descr}{$dbLayout}
503 my $dbLayoutLabel = (defined $DB_LAYOUT_DESCRIPTIONS{$dbLayout}) 525 : "$dbLayout - no description provided in global.conf";
504 ? "$dbLayout - $DB_LAYOUT_DESCRIPTIONS{$dbLayout}"
505 : $dbLayout;
506
507 # we generate singleton radio button tags ourselves because it's too much of a pain to do it with CGI.pm
508 print CGI::Tr( 526 print CGI::Tr({},
509 CGI::td({style=>"text-align: right"}, 527 CGI::td({width=>'20%'}, $dbLayout_buttons{$dbLayout}),
510 '<input type="radio" name="add_dbLayout" value="' . $dbLayout . '"'
511 . ($add_dbLayout eq $dbLayout ? " checked" : "") . ' />',
512 ),
513 CGI::td($dbLayoutLabel), 528 CGI::td($dbLayoutLabel),
514 ); 529 );
515
516 print CGI::start_Tr();
517 print CGI::td(); # for indentation :(
518 print CGI::start_td();
519
520
521 if ($dbLayout eq "sql") {
522
523 print CGI::p({style=>'font-style:italic'},"The following information is only required for the deprecated sql database format:");
524 print CGI::start_table({class=>"FormLayout"});
525 print CGI::Tr(CGI::td({colspan=>2},
526 "Enter the user ID and password for an SQL account with sufficient permissions to create a new database."
527 )
528 );
529 print CGI::Tr(
530 CGI::th({class=>"LeftHeader"}, "SQL Admin Username:"),
531 CGI::td(CGI::textfield("add_sql_username", $add_sql_username, 25)),
532 );
533 print CGI::Tr(
534 CGI::th({class=>"LeftHeader"}, "SQL Admin Password:"),
535 CGI::td(CGI::password_field("add_sql_password", $add_sql_password, 25)),
536 );
537
538 print CGI::Tr(CGI::td({colspan=>2},
539 "The optionial SQL settings you enter below must match the settings in the DBI source"
540 . " specification " . CGI::tt($dbi_source) . ". Replace " . CGI::tt("COURSENAME")
541 . " with the course name you entered above."
542 )
543 );
544 print CGI::Tr(
545 CGI::th({class=>"LeftHeader"}, "SQL Server Host:"),
546 CGI::td(
547 CGI::textfield("add_sql_host", $add_sql_host, 25),
548 CGI::br(),
549 CGI::small("Leave blank to use the default host."),
550 ),
551 );
552 print CGI::Tr(
553 CGI::th({class=>"LeftHeader"}, "SQL Server Port:"),
554 CGI::td(
555 CGI::textfield("add_sql_port", $add_sql_port, 25),
556 CGI::br(),
557 CGI::small("Leave blank to use the default port."),
558 ),
559 );
560
561 print CGI::Tr(
562 CGI::th({class=>"LeftHeader"}, "SQL Database Name:"),
563 CGI::td(
564 CGI::textfield("add_sql_database", $add_sql_database, 25),
565 CGI::br(),
566 CGI::small("Leave blank to use the name ", CGI::tt("webwork_COURSENAME"), "."),
567 ),
568 );
569 print CGI::Tr(
570 CGI::th({class=>"LeftHeader"}, "WeBWorK Host:"),
571 CGI::td(
572 CGI::textfield("add_sql_wwhost", $add_sql_wwhost || "localhost", 25),
573 CGI::br(),
574 CGI::small("If the SQL server does not run on the same host as WeBWorK, enter the host name of the WeBWorK server as seen by the SQL server."),
575 ),
576 );
577 print CGI::end_table();
578 } elsif ($dbLayout eq "gdbm") {
579 print CGI::p({style=>"font-style: italic"},"The following information is only required for the deprecated gdbm database format:");
580 print CGI::start_table({class=>"FormLayout"});
581 print CGI::Tr(
582 CGI::th({class=>"LeftHeader"}, "GDBM Global User ID:"),
583 CGI::td(CGI::textfield("add_gdbm_globalUserID", $add_gdbm_globalUserID || "global_user", 25)),
584 );
585 print CGI::end_table();
586 } 530 }
587
588 print CGI::end_td();
589 print CGI::end_Tr();
590 print CGI::end_table(); 531 print CGI::end_table();
591 }
592
593 print CGI::p({style=>"text-align: center"}, CGI::submit("add_course", "Add Course")); 532 print CGI::p({style=>"text-align: left"}, CGI::submit(-name=>"add_course", -label=>"Add Course"));
594 533
595 print CGI::end_form(); 534 print CGI::end_form();
596} 535}
597 536
598sub add_course_validate { 537sub add_course_validate {
617 my $add_initial_email = $r->param("add_initial_email") || ""; 556 my $add_initial_email = $r->param("add_initial_email") || "";
618 557
619 my $add_templates_course = $r->param("add_templates_course") || ""; 558 my $add_templates_course = $r->param("add_templates_course") || "";
620 559
621 my $add_dbLayout = $r->param("add_dbLayout") || ""; 560 my $add_dbLayout = $r->param("add_dbLayout") || "";
622 my $add_sql_host = $r->param("add_sql_host") || "";
623 my $add_sql_port = $r->param("add_sql_port") || "";
624 my $add_sql_username = $r->param("add_sql_username") || "";
625 my $add_sql_password = $r->param("add_sql_password") || "";
626 my $add_sql_database = $r->param("add_sql_database") || "";
627 my $add_sql_wwhost = $r->param("add_sql_wwhost") || "";
628 my $add_gdbm_globalUserID = $r->param("add_gdbm_globalUserID") || "";
629 561
630 my @errors; 562 my @errors;
631 563
632 if ($add_courseID eq "") { 564 if ($add_courseID eq "") {
633 push @errors, "You must specify a course ID."; 565 push @errors, "You must specify a course ID.";
636 push @errors, "Course ID may only contain letters, numbers, hyphens, and underscores."; 568 push @errors, "Course ID may only contain letters, numbers, hyphens, and underscores.";
637 } 569 }
638 if (grep { $add_courseID eq $_ } listCourses($ce)) { 570 if (grep { $add_courseID eq $_ } listCourses($ce)) {
639 push @errors, "A course with ID $add_courseID already exists."; 571 push @errors, "A course with ID $add_courseID already exists.";
640 } 572 }
641 if ($add_courseTitle eq "") { 573 #if ($add_courseTitle eq "") {
642 push @errors, "You must specify a course title."; 574 # push @errors, "You must specify a course title.";
643 } 575 #}
644 if ($add_courseInstitution eq "") { 576 #if ($add_courseInstitution eq "") {
645 push @errors, "You must specify an institution for this course."; 577 # push @errors, "You must specify an institution for this course.";
646 } 578 #}
647 579
648 if ($add_initial_userID ne "") { 580 if ($add_initial_userID ne "") {
649 if ($add_initial_password eq "") { 581 if ($add_initial_password eq "") {
650 push @errors, "You must specify a password for the initial instructor."; 582 push @errors, "You must specify a password for the initial instructor.";
651 } 583 }
668 600
669 if ($add_dbLayout eq "") { 601 if ($add_dbLayout eq "") {
670 push @errors, "You must select a database layout."; 602 push @errors, "You must select a database layout.";
671 } else { 603 } else {
672 if (exists $ce->{dbLayouts}->{$add_dbLayout}) { 604 if (exists $ce->{dbLayouts}->{$add_dbLayout}) {
673 if ($add_dbLayout eq "sql") { 605 # we used to check for layout-specific fields here, but there aren't any layouts that require them
674 push @errors, "You must specify the SQL admin username." if $add_sql_username eq ""; 606 # anymore. (in the future, we'll probably deal with this in layout-specific modules.)
675 push @errors, "You must specify the WeBWorK host." if $add_sql_wwhost eq "";
676 } elsif ($add_dbLayout eq "gdbm") {
677 push @errors, "You must specify the GDBM global user ID." if $add_gdbm_globalUserID eq "";
678 }
679 } else { 607 } else {
680 push @errors, "The database layout $add_dbLayout doesn't exist."; 608 push @errors, "The database layout $add_dbLayout doesn't exist.";
681 } 609 }
682 } 610 }
683 611
687sub do_add_course { 615sub do_add_course {
688 my ($self) = @_; 616 my ($self) = @_;
689 my $r = $self->r; 617 my $r = $self->r;
690 my $ce = $r->ce; 618 my $ce = $r->ce;
691 my $db = $r->db; 619 my $db = $r->db;
692 #my $authz = $r->authz; 620 my $authz = $r->authz;
693 my $urlpath = $r->urlpath; 621 my $urlpath = $r->urlpath;
694 622
695 my $add_courseID = $r->param("add_courseID") || ""; 623 my $add_courseID = $r->param("add_courseID") || "";
696 my $add_courseTitle = $r->param("add_courseTitle") || ""; 624 my $add_courseTitle = $r->param("add_courseTitle") || "";
697 my $add_courseInstitution = $r->param("add_courseInstitution") || ""; 625 my $add_courseInstitution = $r->param("add_courseInstitution") || "";
706 my $add_initial_email = $r->param("add_initial_email") || ""; 634 my $add_initial_email = $r->param("add_initial_email") || "";
707 635
708 my $add_templates_course = $r->param("add_templates_course") || ""; 636 my $add_templates_course = $r->param("add_templates_course") || "";
709 637
710 my $add_dbLayout = $r->param("add_dbLayout") || ""; 638 my $add_dbLayout = $r->param("add_dbLayout") || "";
711 my $add_sql_host = $r->param("add_sql_host") || "";
712 my $add_sql_port = $r->param("add_sql_port") || "";
713 my $add_sql_username = $r->param("add_sql_username") || "";
714 my $add_sql_password = $r->param("add_sql_password") || "";
715 my $add_sql_database = $r->param("add_sql_database") || "";
716 my $add_sql_wwhost = $r->param("add_sql_wwhost") || "";
717 my $add_gdbm_globalUserID = $r->param("add_gdbm_globalUserID") || "";
718 639
719 my $ce2 = WeBWorK::CourseEnvironment->new( 640 my $ce2 = WeBWorK::CourseEnvironment->new(
720 $ce->{webworkDirs}->{root}, 641 $ce->{webworkDirs}->{root},
721 $ce->{webworkURLs}->{root}, 642 $ce->{webworkURLs}->{root},
722 $ce->{pg}->{directories}->{root}, 643 $ce->{pg}->{directories}->{root},
730 # don't set feedbackRecipients -- this just gets in the way of the more 651 # don't set feedbackRecipients -- this just gets in the way of the more
731 # intelligent "receive_recipients" method. 652 # intelligent "receive_recipients" method.
732 #$courseOptions{feedbackRecipients} = [ $add_initial_email ]; 653 #$courseOptions{feedbackRecipients} = [ $add_initial_email ];
733 } 654 }
734 655
735 if ($add_dbLayout eq "gdbm") { 656 # this is kinda left over from when we had 'gdbm' and 'sql' database layouts
736 $courseOptions{globalUserID} = $add_gdbm_globalUserID if $add_gdbm_globalUserID ne ""; 657 # below this line, we would grab values from getopt and put them in this hash
737 } 658 # but for now the hash can remain empty
738
739 my %dbOptions; 659 my %dbOptions;
740 if ($add_dbLayout eq "sql") {
741 $dbOptions{host} = $add_sql_host if $add_sql_host ne "";
742 $dbOptions{port} = $add_sql_port if $add_sql_port ne "";
743 $dbOptions{username} = $add_sql_username;
744 $dbOptions{password} = $add_sql_password;
745 $dbOptions{database} = $add_sql_database || "webwork_$add_courseID";
746 $dbOptions{wwhost} = $add_sql_wwhost;
747 }
748 660
749 my @users; 661 my @users;
750 662
751 # copy users from current (admin) course if desired 663 # copy users from current (admin) course if desired
752 if ($add_admin_users ne "") { 664 if ($add_admin_users ne "") {
756 next; 668 next;
757 } 669 }
758 my $User = $db->getUser($userID); 670 my $User = $db->getUser($userID);
759 my $Password = $db->getPassword($userID); 671 my $Password = $db->getPassword($userID);
760 my $PermissionLevel = $db->getPermissionLevel($userID); 672 my $PermissionLevel = $db->getPermissionLevel($userID);
761 push @users, [ $User, $Password, $PermissionLevel ]; 673 push @users, [ $User, $Password, $PermissionLevel ]
674 if $authz->hasPermissions($userID,"create_and_delete_courses");
675 #only transfer the "instructors" in the admin course classlist.
762 } 676 }
763 } 677 }
764 678
765 # add initial instructor if desired 679 # add initial instructor if desired
766 if ($add_initial_userID ne "") { 680 if ($add_initial_userID ne "") {
819 } 733 }
820 } else { 734 } else {
821 #log the action 735 #log the action
822 writeLog($ce, "hosted_courses", join("\t", 736 writeLog($ce, "hosted_courses", join("\t",
823 "\tAdded", 737 "\tAdded",
824 $add_courseInstitution, 738 ( defined $add_courseInstitution ? $add_courseInstitution : "(no institution specified)" ),
825 $add_courseTitle, 739 ( defined $add_courseTitle ? $add_courseTitle : "(no title specified)" ),
826 $add_courseID, 740 $add_courseID,
827 $add_initial_firstName, 741 $add_initial_firstName,
828 $add_initial_lastName, 742 $add_initial_lastName,
829 $add_initial_email, 743 $add_initial_email,
830 )); 744 ));
831 # add contact to admin course as student? 745 # add contact to admin course as student?
832 # FIXME -- should we do this? 746 # FIXME -- should we do this?
747 if ($add_initial_userID ne "") {
748 my $composite_id = "${add_initial_userID}_${add_courseID}"; # student id includes school name and contact
749 my $User = $db->newUser(
750 user_id => $composite_id, # student id includes school name and contact
751 first_name => $add_initial_firstName,
752 last_name => $add_initial_lastName,
753 student_id => $add_initial_userID,
754 email_address => $add_initial_email,
755 status => "C",
756 );
757 my $Password = $db->newPassword(
758 user_id => $composite_id,
759 password => cryptPassword($add_initial_password),
760 );
761 my $PermissionLevel = $db->newPermissionLevel(
762 user_id => $composite_id,
763 permission => "0",
764 );
765 # add contact to admin course as student
766 # or if this contact and course already exist in a dropped status
767 # change the student's status to enrolled
768 if (my $oldUser = $db->getUser($composite_id) ) {
769 warn "Replacing old data for $composite_id status: ". $oldUser->status;
770 $db->deleteUser($composite_id);
771 }
772 eval { $db->addUser($User) }; warn $@ if $@;
773 eval { $db->addPassword($Password) }; warn $@ if $@;
774 eval { $db->addPermissionLevel($PermissionLevel) }; warn $@ if $@;
775 }
833 print CGI::div({class=>"ResultsWithoutError"}, 776 print CGI::div({class=>"ResultsWithoutError"},
834 CGI::p("Successfully created the course $add_courseID"), 777 CGI::p("Successfully created the course $add_courseID"),
835 ); 778 );
836 my $newCoursePath = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", 779 my $newCoursePath = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets",
837 courseID => $add_courseID); 780 courseID => $add_courseID);
854 #my $authz = $r->authz; 797 #my $authz = $r->authz;
855 #my $urlpath = $r->urlpath; 798 #my $urlpath = $r->urlpath;
856 799
857 my $rename_oldCourseID = $r->param("rename_oldCourseID") || ""; 800 my $rename_oldCourseID = $r->param("rename_oldCourseID") || "";
858 my $rename_newCourseID = $r->param("rename_newCourseID") || ""; 801 my $rename_newCourseID = $r->param("rename_newCourseID") || "";
859
860 my $rename_sql_host = $r->param("rename_sql_host") || "";
861 my $rename_sql_port = $r->param("rename_sql_port") || "";
862 my $rename_sql_username = $r->param("rename_sql_username") || "";
863 my $rename_sql_password = $r->param("rename_sql_password") || "";
864 my $rename_sql_oldDatabase = $r->param("rename_sql_oldDatabase") || "";
865 my $rename_sql_newDatabase = $r->param("rename_sql_newDatabase") || "";
866 my $rename_sql_wwhost = $r->param("rename_sql_wwhost") || "";
867 802
868 my @courseIDs = listCourses($ce); 803 my @courseIDs = listCourses($ce);
869 @courseIDs = sort {lc($a) cmp lc ($b) } @courseIDs; 804 @courseIDs = sort {lc($a) cmp lc ($b) } @courseIDs;
870 805
871 my %courseLabels; # records... heh. 806 my %courseLabels; # records... heh.
879 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")"; 814 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
880 } 815 }
881 816
882 print CGI::h2("Rename Course"); 817 print CGI::h2("Rename Course");
883 818
884 print CGI::start_form("POST", $r->uri); 819 print CGI::start_form(-method=>"POST", -action=>$r->uri);
885 print $self->hidden_authen_fields; 820 print $self->hidden_authen_fields;
886 print $self->hidden_fields("subDisplay"); 821 print $self->hidden_fields("subDisplay");
887 822
888 print CGI::p("Select a course to rename."); 823 print CGI::p("Select a course to rename.");
889 824
890 print CGI::table({class=>"FormLayout"}, 825 print CGI::table({class=>"FormLayout"},
891 CGI::Tr( 826 CGI::Tr({},
892 CGI::th({class=>"LeftHeader"}, "Course Name:"), 827 CGI::th({class=>"LeftHeader"}, "Course Name:"),
893 CGI::td( 828 CGI::td(
894 CGI::scrolling_list( 829 CGI::scrolling_list(
895 -name => "rename_oldCourseID", 830 -name => "rename_oldCourseID",
896 -values => \@courseIDs, 831 -values => \@courseIDs,
899 -multiple => 0, 834 -multiple => 0,
900 -labels => \%courseLabels, 835 -labels => \%courseLabels,
901 ), 836 ),
902 ), 837 ),
903 ), 838 ),
904 CGI::Tr( 839 CGI::Tr({},
905 CGI::th({class=>"LeftHeader"}, "New Name:"), 840 CGI::th({class=>"LeftHeader"}, "New Name:"),
906 CGI::td(CGI::textfield("rename_newCourseID", $rename_newCourseID, 25)), 841 CGI::td(CGI::textfield(-name=>"rename_newCourseID", -value=>$rename_newCourseID, -size=>25)),
907 ), 842 ),
908 ); 843 );
909 844
910 print CGI::p(
911 "If the course's database layout (indicated in parentheses above) is "
912 . CGI::b("sql") . ", supply the SQL connections information requested below."
913 );
914
915 print CGI::start_table({class=>"FormLayout"});
916 print CGI::Tr(CGI::td({colspan=>2},
917 "Enter the user ID and password for an SQL account with sufficient permissions to create and delete databases."
918 )
919 );
920 print CGI::Tr(
921 CGI::th({class=>"LeftHeader"}, "SQL Admin Username:"),
922 CGI::td(CGI::textfield("rename_sql_username", $rename_sql_username, 25)),
923 );
924 print CGI::Tr(
925 CGI::th({class=>"LeftHeader"}, "SQL Admin Password:"),
926 CGI::td(CGI::password_field("rename_sql_password", $rename_sql_password, 25)),
927 );
928
929 print CGI::Tr(
930 CGI::th({class=>"LeftHeader"}, "SQL Server Host:"),
931 CGI::td(
932 CGI::textfield("rename_sql_host", $rename_sql_host, 25),
933 CGI::br(),
934 CGI::small("Leave blank to use the default host."),
935 ),
936 );
937 print CGI::Tr(
938 CGI::th({class=>"LeftHeader"}, "SQL Server Port:"),
939 CGI::td(
940 CGI::textfield("rename_sql_port", $rename_sql_port, 25),
941 CGI::br(),
942 CGI::small("Leave blank to use the default port."),
943 ),
944 );
945
946 print CGI::Tr(
947 CGI::th({class=>"LeftHeader"}, "SQL Current Database Name:"),
948 CGI::td(
949 CGI::textfield("rename_sql_database", $rename_sql_oldDatabase, 25),
950 CGI::br(),
951 CGI::small("Leave blank to use the name ", CGI::tt("webwork_COURSENAME"), "."),
952 ),
953 );
954 print CGI::Tr(
955 CGI::th({class=>"LeftHeader"}, "SQL New Database Name:"),
956 CGI::td(
957 CGI::textfield("rename_sql_database", $rename_sql_newDatabase, 25),
958 CGI::br(),
959 CGI::small("Leave blank to use the name ", CGI::tt("webwork_COURSENAME"), "."),
960 ),
961 );
962 print CGI::Tr(
963 CGI::th({class=>"LeftHeader"}, "WeBWorK Host:"),
964 CGI::td(
965 CGI::textfield("rename_sql_wwhost", $rename_sql_wwhost || "localhost", 25),
966 CGI::br(),
967 CGI::small("If the SQL server does not run on the same host as WeBWorK, enter the host name of the WeBWorK server as seen by the SQL server."),
968 ),
969 );
970 print CGI::end_table(); 845 print CGI::end_table();
971 846
972 print CGI::p({style=>"text-align: center"}, CGI::submit("rename_course", "Rename Course")); 847 print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"rename_course", -label=>"Rename Course"));
973 848
974 print CGI::end_form(); 849 print CGI::end_form();
975} 850}
976 851
977sub rename_course_validate { 852sub rename_course_validate {
982 #my $authz = $r->authz; 857 #my $authz = $r->authz;
983 #my $urlpath = $r->urlpath; 858 #my $urlpath = $r->urlpath;
984 859
985 my $rename_oldCourseID = $r->param("rename_oldCourseID") || ""; 860 my $rename_oldCourseID = $r->param("rename_oldCourseID") || "";
986 my $rename_newCourseID = $r->param("rename_newCourseID") || ""; 861 my $rename_newCourseID = $r->param("rename_newCourseID") || "";
987
988 my $rename_sql_host = $r->param("rename_sql_host") || "";
989 my $rename_sql_port = $r->param("rename_sql_port") || "";
990 my $rename_sql_username = $r->param("rename_sql_username") || "";
991 my $rename_sql_password = $r->param("rename_sql_password") || "";
992 my $rename_sql_oldDatabase = $r->param("rename_sql_oldDatabase") || "";
993 my $rename_sql_newDatabase = $r->param("rename_sql_newDatabase") || "";
994 my $rename_sql_wwhost = $r->param("rename_sql_wwhost") || "";
995 862
996 my @errors; 863 my @errors;
997 864
998 if ($rename_oldCourseID eq "") { 865 if ($rename_oldCourseID eq "") {
999 push @errors, "You must select a course to rename."; 866 push @errors, "You must select a course to rename.";
1016 $ce->{webworkURLs}->{root}, 883 $ce->{webworkURLs}->{root},
1017 $ce->{pg}->{directories}->{root}, 884 $ce->{pg}->{directories}->{root},
1018 $rename_oldCourseID, 885 $rename_oldCourseID,
1019 ); 886 );
1020 887
1021 if ($ce2->{dbLayoutName} eq "sql") {
1022 push @errors, "You must specify the SQL admin username." if $rename_sql_username eq "";
1023 #push @errors, "You must specify the SQL admin password." if $rename_sql_password eq "";
1024 #push @errors, "You must specify the current SQL database name." if $rename_sql_oldDatabase eq "";
1025 #push @errors, "You must specify the new SQL database name." if $rename_sql_newDatabase eq "";
1026 }
1027
1028 return @errors; 888 return @errors;
1029} 889}
1030 890
1031sub do_rename_course { 891sub do_rename_course {
1032 my ($self) = @_; 892 my ($self) = @_;
1037 my $urlpath = $r->urlpath; 897 my $urlpath = $r->urlpath;
1038 898
1039 my $rename_oldCourseID = $r->param("rename_oldCourseID") || ""; 899 my $rename_oldCourseID = $r->param("rename_oldCourseID") || "";
1040 my $rename_newCourseID = $r->param("rename_newCourseID") || ""; 900 my $rename_newCourseID = $r->param("rename_newCourseID") || "";
1041 901
1042 my $rename_sql_host = $r->param("rename_sql_host") || "";
1043 my $rename_sql_port = $r->param("rename_sql_port") || "";
1044 my $rename_sql_username = $r->param("rename_sql_username") || "";
1045 my $rename_sql_password = $r->param("rename_sql_password") || "";
1046 my $rename_sql_oldDatabase = $r->param("rename_sql_oldDatabase") || "";
1047 my $rename_sql_newDatabase = $r->param("rename_sql_newDatabase") || "";
1048 my $rename_sql_wwhost = $r->param("rename_sql_wwhost") || "";
1049
1050 my $ce2 = WeBWorK::CourseEnvironment->new( 902 my $ce2 = WeBWorK::CourseEnvironment->new(
1051 $ce->{webworkDirs}->{root}, 903 $ce->{webworkDirs}->{root},
1052 $ce->{webworkURLs}->{root}, 904 $ce->{webworkURLs}->{root},
1053 $ce->{pg}->{directories}->{root}, 905 $ce->{pg}->{directories}->{root},
1054 $rename_oldCourseID, 906 $rename_oldCourseID,
1055 ); 907 );
1056 908
1057 my $dbLayoutName = $ce->{dbLayoutName}; 909 my $dbLayoutName = $ce->{dbLayoutName};
1058 910
911 # this is kinda left over from when we had 'gdbm' and 'sql' database layouts
912 # below this line, we would grab values from getopt and put them in this hash
913 # but for now the hash can remain empty
1059 my %dbOptions; 914 my %dbOptions;
1060 if ($dbLayoutName eq "sql") {
1061 $dbOptions{host} = $rename_sql_host if $rename_sql_host ne "";
1062 $dbOptions{port} = $rename_sql_port if $rename_sql_port ne "";
1063 $dbOptions{username} = $rename_sql_username;
1064 $dbOptions{password} = $rename_sql_password;
1065 $dbOptions{old_database} = $rename_sql_oldDatabase || "webwork_$rename_oldCourseID";
1066 $dbOptions{new_database} = $rename_sql_newDatabase || "webwork_$rename_newCourseID";
1067 $dbOptions{wwhost} = $rename_sql_wwhost;
1068 }
1069 915
1070 eval { 916 eval {
1071 renameCourse( 917 renameCourse(
1072 courseID => $rename_oldCourseID, 918 courseID => $rename_oldCourseID,
1073 ce => $ce2, 919 ce => $ce2,
1103 #my $db = $r->db; 949 #my $db = $r->db;
1104 #my $authz = $r->authz; 950 #my $authz = $r->authz;
1105 #my $urlpath = $r->urlpath; 951 #my $urlpath = $r->urlpath;
1106 952
1107 my $delete_courseID = $r->param("delete_courseID") || ""; 953 my $delete_courseID = $r->param("delete_courseID") || "";
1108 my $delete_sql_host = $r->param("delete_sql_host") || "";
1109 my $delete_sql_port = $r->param("delete_sql_port") || "";
1110 my $delete_sql_username = $r->param("delete_sql_username") || "";
1111 my $delete_sql_password = $r->param("delete_sql_password") || "";
1112 my $delete_sql_database = $r->param("delete_sql_database") || "";
1113 954
1114 my @courseIDs = listCourses($ce); 955 my @courseIDs = listCourses($ce);
1115 @courseIDs = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive 956 @courseIDs = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive
1116 957
1117 my %courseLabels; # records... heh. 958 my %courseLabels; # records... heh.
1125 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")"; 966 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
1126 } 967 }
1127 968
1128 print CGI::h2("Delete Course"); 969 print CGI::h2("Delete Course");
1129 970
1130 print CGI::start_form("POST", $r->uri); 971 print CGI::start_form(-method=>"POST", -action=>$r->uri);
1131 print $self->hidden_authen_fields; 972 print $self->hidden_authen_fields;
1132 print $self->hidden_fields("subDisplay"); 973 print $self->hidden_fields("subDisplay");
1133 974
1134 print CGI::p("Select a course to delete."); 975 print CGI::p("Select a course to delete.");
1135 976
1136 print CGI::table({class=>"FormLayout"}, 977 print CGI::table({class=>"FormLayout"},
1137 CGI::Tr( 978 CGI::Tr({},
1138 CGI::th({class=>"LeftHeader"}, "Course Name:"), 979 CGI::th({class=>"LeftHeader"}, "Course Name:"),
1139 CGI::td( 980 CGI::td(
1140 CGI::scrolling_list( 981 CGI::scrolling_list(
1141 -name => "delete_courseID", 982 -name => "delete_courseID",
1142 -values => \@courseIDs, 983 -values => \@courseIDs,
1147 ), 988 ),
1148 ), 989 ),
1149 ), 990 ),
1150 ); 991 );
1151 992
1152 print CGI::p(
1153 "If the course's database layout (indicated in parentheses above) is "
1154 . CGI::b("sql") . ", supply the SQL connections information requested below."
1155 );
1156
1157 print CGI::start_table({class=>"FormLayout"});
1158 print CGI::Tr(CGI::td({colspan=>2},
1159 "Enter the user ID and password for an SQL account with sufficient permissions to delete an existing database."
1160 )
1161 );
1162 print CGI::Tr(
1163 CGI::th({class=>"LeftHeader"}, "SQL Admin Username:"),
1164 CGI::td(CGI::textfield("delete_sql_username", $delete_sql_username, 25)),
1165 );
1166 print CGI::Tr(
1167 CGI::th({class=>"LeftHeader"}, "SQL Admin Password:"),
1168 CGI::td(CGI::password_field("delete_sql_password", $delete_sql_password, 25)),
1169 );
1170
1171 #print CGI::Tr(CGI::td({colspan=>2},
1172 # "The optionial SQL settings you enter below must match the settings in the DBI source"
1173 # . " specification " . CGI::tt($dbi_source) . ". Replace " . CGI::tt("COURSENAME")
1174 # . " with the course name you entered above."
1175 # )
1176 #);
1177 print CGI::Tr(
1178 CGI::th({class=>"LeftHeader"}, "SQL Server Host:"),
1179 CGI::td(
1180 CGI::textfield("delete_sql_host", $delete_sql_host, 25),
1181 CGI::br(),
1182 CGI::small("Leave blank to use the default host."),
1183 ),
1184 );
1185 print CGI::Tr(
1186 CGI::th({class=>"LeftHeader"}, "SQL Server Port:"),
1187 CGI::td(
1188 CGI::textfield("delete_sql_port", $delete_sql_port, 25),
1189 CGI::br(),
1190 CGI::small("Leave blank to use the default port."),
1191 ),
1192 );
1193
1194 print CGI::Tr(
1195 CGI::th({class=>"LeftHeader"}, "SQL Database Name:"),
1196 CGI::td(
1197 CGI::textfield("delete_sql_database", $delete_sql_database, 25),
1198 CGI::br(),
1199 CGI::small("Leave blank to use the name ", CGI::tt("webwork_COURSENAME"), "."),
1200 ),
1201 );
1202 print CGI::end_table();
1203
1204 print CGI::p({style=>"text-align: center"}, CGI::submit("delete_course", "Delete Course")); 993 print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"delete_course", -value=>"Delete Course"));
1205 994
1206 print CGI::end_form(); 995 print CGI::end_form();
1207} 996}
1208 997
1209sub delete_course_validate { 998sub delete_course_validate {
1213 #my $db = $r->db; 1002 #my $db = $r->db;
1214 #my $authz = $r->authz; 1003 #my $authz = $r->authz;
1215 my $urlpath = $r->urlpath; 1004 my $urlpath = $r->urlpath;
1216 1005
1217 my $delete_courseID = $r->param("delete_courseID") || ""; 1006 my $delete_courseID = $r->param("delete_courseID") || "";
1218 my $delete_sql_host = $r->param("delete_sql_host") || "";
1219 my $delete_sql_port = $r->param("delete_sql_port") || "";
1220 my $delete_sql_username = $r->param("delete_sql_username") || "";
1221 my $delete_sql_password = $r->param("delete_sql_password") || "";
1222 my $delete_sql_database = $r->param("delete_sql_database") || "";
1223 1007
1224 my @errors; 1008 my @errors;
1225 1009
1226 if ($delete_courseID eq "") { 1010 if ($delete_courseID eq "") {
1227 push @errors, "You must specify a course name."; 1011 push @errors, "You must specify a course name.";
1234 $ce->{webworkURLs}->{root}, 1018 $ce->{webworkURLs}->{root},
1235 $ce->{pg}->{directories}->{root}, 1019 $ce->{pg}->{directories}->{root},
1236 $delete_courseID, 1020 $delete_courseID,
1237 ); 1021 );
1238 1022
1239 if ($ce2->{dbLayoutName} eq "sql") {
1240 push @errors, "You must specify the SQL admin username." if $delete_sql_username eq "";
1241 #push @errors, "You must specify the SQL admin password." if $delete_sql_password eq "";
1242 #push @errors, "You must specify the SQL database name." if $delete_sql_database eq "";
1243 }
1244
1245 return @errors; 1023 return @errors;
1246} 1024}
1247 1025
1248sub delete_course_confirm { 1026sub delete_course_confirm {
1249 my ($self) = @_; 1027 my ($self) = @_;
1254 #my $urlpath = $r->urlpath; 1032 #my $urlpath = $r->urlpath;
1255 1033
1256 print CGI::h2("Delete Course"); 1034 print CGI::h2("Delete Course");
1257 1035
1258 my $delete_courseID = $r->param("delete_courseID") || ""; 1036 my $delete_courseID = $r->param("delete_courseID") || "";
1259 my $delete_sql_host = $r->param("delete_sql_host") || "";
1260 my $delete_sql_port = $r->param("delete_sql_port") || "";
1261 my $delete_sql_database = $r->param("delete_sql_database") || "";
1262 1037
1263 my $ce2 = WeBWorK::CourseEnvironment->new( 1038 my $ce2 = WeBWorK::CourseEnvironment->new(
1264 $ce->{webworkDirs}->{root}, 1039 $ce->{webworkDirs}->{root},
1265 $ce->{webworkURLs}->{root}, 1040 $ce->{webworkURLs}->{root},
1266 $ce->{pg}->{directories}->{root}, 1041 $ce->{pg}->{directories}->{root},
1267 $delete_courseID, 1042 $delete_courseID,
1268 ); 1043 );
1269 1044
1270 if ($ce2->{dbLayoutName} eq "sql") {
1271 print CGI::p("Are you sure you want to delete the course " . CGI::b($delete_courseID) 1045 print CGI::p("Are you sure you want to delete the course " . CGI::b($delete_courseID)
1272 . "? All course files and data and the following database will be destroyed."
1273 . " There is no undo available.");
1274
1275 print CGI::table({class=>"FormLayout"},
1276 CGI::Tr(
1277 CGI::th({class=>"LeftHeader"}, "SQL Server Host:"),
1278 CGI::td($delete_sql_host || "system default"),
1279 ),
1280 CGI::Tr(
1281 CGI::th({class=>"LeftHeader"}, "SQL Server Port:"),
1282 CGI::td($delete_sql_port || "system default"),
1283 ),
1284 CGI::Tr(
1285 CGI::th({class=>"LeftHeader"}, "SQL Database Name:"),
1286 CGI::td($delete_sql_database || "webwork_$delete_courseID"),
1287 ),
1288 );
1289 } else {
1290 print CGI::p("Are you sure you want to delete the course " . CGI::b($delete_courseID)
1291 . "? All course files and data will be destroyed. There is no undo available."); 1046 . "? All course files and data will be destroyed. There is no undo available.");
1292 }
1293 1047
1294 print CGI::start_form("POST", $r->uri); 1048 print CGI::start_form(-method=>"POST", -action=>$r->uri);
1295 print $self->hidden_authen_fields; 1049 print $self->hidden_authen_fields;
1296 print $self->hidden_fields("subDisplay"); 1050 print $self->hidden_fields("subDisplay");
1297 print $self->hidden_fields(qw/delete_courseID delete_sql_host delete_sql_port delete_sql_username delete_sql_password delete_sql_database/); 1051 print $self->hidden_fields(qw/delete_courseID/);
1298 1052
1299 print CGI::p({style=>"text-align: center"}, 1053 print CGI::p({style=>"text-align: center"},
1300 CGI::submit("decline_delete_course", "Don't delete"), 1054 CGI::submit(-name=>"decline_delete_course", -label=>"Don't delete"),
1301 "&nbsp;", 1055 "&nbsp;",
1302 CGI::submit("confirm_delete_course", "Delete"), 1056 CGI::submit(-name=>"confirm_delete_course", -label=>"Delete"),
1303 ); 1057 );
1304 1058
1305 print CGI::end_form(); 1059 print CGI::end_form();
1306} 1060}
1307 1061
1308sub do_delete_course { 1062sub do_delete_course {
1309 my ($self) = @_; 1063 my ($self) = @_;
1310 my $r = $self->r; 1064 my $r = $self->r;
1311 my $ce = $r->ce; 1065 my $ce = $r->ce;
1312 #my $db = $r->db; 1066 my $db = $r->db;
1313 #my $authz = $r->authz; 1067 #my $authz = $r->authz;
1314 #my $urlpath = $r->urlpath; 1068 #my $urlpath = $r->urlpath;
1315 1069
1316 my $delete_courseID = $r->param("delete_courseID") || ""; 1070 my $delete_courseID = $r->param("delete_courseID") || "";
1317 my $delete_sql_host = $r->param("delete_sql_host") || "";
1318 my $delete_sql_port = $r->param("delete_sql_port") || "";
1319 my $delete_sql_username = $r->param("delete_sql_username") || "";
1320 my $delete_sql_password = $r->param("delete_sql_password") || "";
1321 my $delete_sql_database = $r->param("delete_sql_database") || "";
1322 1071
1323 my $ce2 = WeBWorK::CourseEnvironment->new( 1072 my $ce2 = WeBWorK::CourseEnvironment->new(
1324 $ce->{webworkDirs}->{root}, 1073 $ce->{webworkDirs}->{root},
1325 $ce->{webworkURLs}->{root}, 1074 $ce->{webworkURLs}->{root},
1326 $ce->{pg}->{directories}->{root}, 1075 $ce->{pg}->{directories}->{root},
1327 $delete_courseID, 1076 $delete_courseID,
1328 ); 1077 );
1329 1078
1079 # this is kinda left over from when we had 'gdbm' and 'sql' database layouts
1080 # below this line, we would grab values from getopt and put them in this hash
1081 # but for now the hash can remain empty
1330 my %dbOptions; 1082 my %dbOptions;
1331 if ($ce2->{dbLayoutName} eq "sql") {
1332 $dbOptions{host} = $delete_sql_host if $delete_sql_host ne "";
1333 $dbOptions{port} = $delete_sql_port if $delete_sql_port ne "";
1334 $dbOptions{username} = $delete_sql_username;
1335 $dbOptions{password} = $delete_sql_password;
1336 $dbOptions{database} = $delete_sql_database || "webwork_$delete_courseID";
1337 }
1338 1083
1339 eval { 1084 eval {
1340 deleteCourse( 1085 deleteCourse(
1341 courseID => $delete_courseID, 1086 courseID => $delete_courseID,
1342 ce => $ce2, 1087 ce => $ce2,
1349 print CGI::div({class=>"ResultsWithError"}, 1094 print CGI::div({class=>"ResultsWithError"},
1350 CGI::p("An error occured while deleting the course $delete_courseID:"), 1095 CGI::p("An error occured while deleting the course $delete_courseID:"),
1351 CGI::tt(CGI::escapeHTML($error)), 1096 CGI::tt(CGI::escapeHTML($error)),
1352 ); 1097 );
1353 } else { 1098 } else {
1099 # mark the contact person in the admin course as dropped.
1100 # find the contact person for the course by searching the admin classlist.
1101 my @contacts = grep /_$delete_courseID$/, $db->listUsers;
1102 if (@contacts) {
1103 die "Incorrect number of contacts for the course $delete_courseID". join(" ", @contacts) if @contacts !=1;
1104 #warn "contacts", join(" ", @contacts);
1105 #my $composite_id = "${add_initial_userID}_${add_courseID}";
1106 my $composite_id = $contacts[0];
1107
1108 # mark the contact person as dropped.
1109 my $User = $db->getUser($composite_id);
1110 my $status_name = 'Drop';
1111 my $status_value = ($ce->status_name_to_abbrevs($status_name))[0];
1112 $User->status($status_value);
1113 $db->putUser($User);
1114 }
1115
1354 print CGI::div({class=>"ResultsWithoutError"}, 1116 print CGI::div({class=>"ResultsWithoutError"},
1355 CGI::p("Successfully deleted the course $delete_courseID."), 1117 CGI::p("Successfully deleted the course $delete_courseID."),
1356 ); 1118 );
1357 writeLog($ce, "hosted_courses", join("\t", 1119 writeLog($ce, "hosted_courses", join("\t",
1358 "\tDeleted", 1120 "\tDeleted",
1359 "", 1121 "",
1360 "", 1122 "",
1361 $delete_courseID, 1123 $delete_courseID,
1362 )); 1124 ));
1363 print CGI::start_form("POST", $r->uri); 1125 print CGI::start_form(-method=>"POST", -action=>$r->uri);
1364 print $self->hidden_authen_fields; 1126 print $self->hidden_authen_fields;
1365 print $self->hidden_fields("subDisplay"); 1127 print $self->hidden_fields("subDisplay");
1366 1128
1367 print CGI::p({style=>"text-align: center"}, CGI::submit("decline_delete_course", "OK"),); 1129 print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"decline_delete_course", -value=>"OK"),);
1368 1130
1369 print CGI::end_form(); 1131 print CGI::end_form();
1370 } 1132 }
1371} 1133}
1372 1134
1401 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")"; 1163 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
1402 } 1164 }
1403 1165
1404 print CGI::h2("Export Database"); 1166 print CGI::h2("Export Database");
1405 1167
1168 print CGI::p(IMPORT_EXPORT_WARNING);
1169
1406 print CGI::start_form("GET", $r->uri); 1170 print CGI::start_form(-method=>"GET", -action=>$r->uri);
1407 print $self->hidden_authen_fields; 1171 print $self->hidden_authen_fields;
1408 print $self->hidden_fields("subDisplay"); 1172 print $self->hidden_fields("subDisplay");
1409 1173
1410 print CGI::p("Select a course to export the course's database. Please note 1174 print CGI::p({},"Select a course to export the course's database. Please note
1411 that exporting can take a very long time for a large course. If you have 1175 that exporting can take a very long time for a large course. If you have
1412 shell access to the WeBWorK server, you may use the ", CGI::code("wwdb"), " 1176 shell access to the WeBWorK server, you may use the ", CGI::code("wwdb"), "
1413 utility instead."); 1177 utility instead.");
1414 1178
1415 print CGI::table({class=>"FormLayout"}, 1179 print CGI::table({class=>"FormLayout"},
1416 CGI::Tr( 1180 CGI::Tr({},
1417 CGI::th({class=>"LeftHeader"}, "Course Name:"), 1181 CGI::th({class=>"LeftHeader"}, "Course Name:"),
1418 CGI::td( 1182 CGI::td(
1419 CGI::scrolling_list( 1183 CGI::scrolling_list(
1420 -name => "export_courseID", 1184 -name => "export_courseID",
1421 -values => \@courseIDs, 1185 -values => \@courseIDs,
1424 -multiple => 1, 1188 -multiple => 1,
1425 -labels => \%courseLabels, 1189 -labels => \%courseLabels,
1426 ), 1190 ),
1427 ), 1191 ),
1428 ), 1192 ),
1429 CGI::Tr( 1193 CGI::Tr({},
1430 CGI::th({class=>"LeftHeader"}, "Tables to Export:"), 1194 CGI::th({class=>"LeftHeader"}, "Tables to Export:"),
1431 CGI::td( 1195 CGI::td({},
1432 CGI::checkbox_group( 1196 CGI::checkbox_group(
1433 -name => "export_tables", 1197 -name => "export_tables",
1434 -values => \@tables, 1198 -values => \@tables,
1435 -default => \@export_tables, 1199 -default => \@export_tables,
1436 -linebreak => 1, 1200 -linebreak => 1,
1437 ), 1201 ),
1438 ), 1202 ),
1439 ), 1203 ),
1440 ); 1204 );
1441 1205
1442 print CGI::p({style=>"text-align: center"}, CGI::submit("export_database", "Export Database")); 1206 print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"export_database", -value=>"Export Database"));
1443 1207
1444 print CGI::end_form(); 1208 print CGI::end_form();
1445} 1209}
1446 1210
1447sub export_database_validate { 1211sub export_database_validate {
1586 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")"; 1350 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
1587 } 1351 }
1588 1352
1589 # find databases: 1353 # find databases:
1590 my $templatesDir = $ce->{courseDirs}->{templates}; 1354 my $templatesDir = $ce->{courseDirs}->{templates};
1591 my %probLibs = %{ $r->ce->{courseFiles}->{problibs} }; 1355 my $exempt_dirs = join("|", keys %{ $r->ce->{courseFiles}->{problibs} });
1592 my $exempt_dirs = join("|", keys %probLibs);
1593 1356
1594 my @databaseFiles = listFilesRecursive( 1357 my @databaseFiles = listFilesRecursive(
1595 $templatesDir, 1358 $templatesDir,
1596 qr/.\.exported\.xml\.\d*\.gz$/, # match these files #FIXME this is too restricive!! 1359 qr/.\.exported\.xml\.\d*\.gz$/, # match these files #FIXME this is too restricive!!
1597 qr/^(?:$exempt_dirs|CVS)$/, # prune these directories 1360 qr/^(?:$exempt_dirs|CVS)$/, # prune these directories
1603 1366
1604 ####### 1367 #######
1605 1368
1606 print CGI::h2("Import Database"); 1369 print CGI::h2("Import Database");
1607 1370
1371 print CGI::p(IMPORT_EXPORT_WARNING);
1372
1608 print CGI::start_form("POST", $r->uri, &CGI::MULTIPART); 1373 print CGI::start_form(-method=>"POST", -action=>$r->uri, -enctype=>&CGI::MULTIPART);
1609 print $self->hidden_authen_fields; 1374 print $self->hidden_authen_fields;
1610 print $self->hidden_fields("subDisplay"); 1375 print $self->hidden_fields("subDisplay");
1611 1376
1612 print CGI::table({class=>"FormLayout"}, 1377 print CGI::table({class=>"FormLayout"},
1613 CGI::Tr( 1378 CGI::Tr({},
1614 CGI::th({class=>"LeftHeader"}, "Database XML File:"), 1379 CGI::th({class=>"LeftHeader"}, "Database XML File:"),
1615# CGI::td(
1616# CGI::filefield(
1617# -name => "import_file",
1618# -size => 50,
1619# ),
1620# ),
1621 CGI::td( 1380 CGI::td(
1622 CGI::scrolling_list( 1381 CGI::scrolling_list(
1623 -name => "import_file", 1382 -name => "import_file",
1624 -values => \@databaseFiles, 1383 -values => \@databaseFiles,
1625 -default => undef, 1384 -default => undef,
1628 -labels => \%databaseLabels, 1387 -labels => \%databaseLabels,
1629 ), 1388 ),
1630 1389
1631 ) 1390 )
1632 ), 1391 ),
1633 CGI::Tr( 1392 CGI::Tr({},
1634 CGI::th({class=>"LeftHeader"}, "Tables to Import:"), 1393 CGI::th({class=>"LeftHeader"}, "Tables to Import:"),
1635 CGI::td( 1394 CGI::td(
1636 CGI::checkbox_group( 1395 CGI::checkbox_group(
1637 -name => "import_tables", 1396 -name => "import_tables",
1638 -values => \@tables, 1397 -values => \@tables,
1639 -default => \@import_tables, 1398 -default => \@import_tables,
1640 -linebreak => 1, 1399 -linebreak => 1,
1641 ), 1400 ),
1642 ), 1401 ),
1643 ), 1402 ),
1644 CGI::Tr( 1403 CGI::Tr({},
1645 CGI::th({class=>"LeftHeader"}, "Import into Course:"), 1404 CGI::th({class=>"LeftHeader"}, "Import into Course:"),
1646 CGI::td( 1405 CGI::td(
1647 CGI::scrolling_list( 1406 CGI::scrolling_list(
1648 -name => "import_courseID", 1407 -name => "import_courseID",
1649 -values => \@courseIDs, 1408 -values => \@courseIDs,
1652 -multiple => 0, 1411 -multiple => 0,
1653 -labels => \%courseLabels, 1412 -labels => \%courseLabels,
1654 ), 1413 ),
1655 ), 1414 ),
1656 ), 1415 ),
1657 CGI::Tr( 1416 CGI::Tr({},
1658 CGI::th({class=>"LeftHeader"}, "Conflicts:"), 1417 CGI::th({class=>"LeftHeader"}, "Conflicts:"),
1659 CGI::td( 1418 CGI::td(
1660 CGI::radio_group( 1419 CGI::radio_group(
1661 -name => "import_conflict", 1420 -name => "import_conflict",
1662 -values => [qw/skip replace/], 1421 -values => [qw/skip replace/],
1669 ), 1428 ),
1670 ), 1429 ),
1671 ), 1430 ),
1672 ); 1431 );
1673 1432
1674 print CGI::p({style=>"text-align: center"}, CGI::submit("import_database", "Import Database")); 1433 print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"import_database", -value=>"Import Database"));
1675 1434
1676 print CGI::end_form(); 1435 print CGI::end_form();
1677} 1436}
1678 1437
1679sub import_database_validate { 1438sub import_database_validate {
1778 #my $db = $r->db; 1537 #my $db = $r->db;
1779 #my $authz = $r->authz; 1538 #my $authz = $r->authz;
1780 #my $urlpath = $r->urlpath; 1539 #my $urlpath = $r->urlpath;
1781 1540
1782 my $archive_courseID = $r->param("archive_courseID") || ""; 1541 my $archive_courseID = $r->param("archive_courseID") || "";
1783 my $archive_sql_host = $r->param("archive_sql_host") || "";
1784 my $archive_sql_port = $r->param("archive_sql_port") || "";
1785 my $archive_sql_username = $r->param("archive_sql_username") || "";
1786 my $archive_sql_password = $r->param("archive_sql_password") || "";
1787 my $archive_sql_database = $r->param("archive_sql_database") || "";
1788 1542
1789 my @courseIDs = listCourses($ce); 1543 my @courseIDs = listCourses($ce);
1790 @courseIDs = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive 1544 @courseIDs = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive
1791 1545
1792 my %courseLabels; # records... heh. 1546 my %courseLabels; # records... heh.
1800 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")"; 1554 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
1801 } 1555 }
1802 1556
1803 print CGI::h2("archive Course"); 1557 print CGI::h2("archive Course");
1804 1558
1805 print CGI::start_form("POST", $r->uri); 1559 print CGI::start_form(-method=>"POST", -action=>$r->uri);
1806 print $self->hidden_authen_fields; 1560 print $self->hidden_authen_fields;
1807 print $self->hidden_fields("subDisplay"); 1561 print $self->hidden_fields("subDisplay");
1808 1562
1809 print CGI::p("Select a course to archive."); 1563 print CGI::p("Select a course to archive.");
1810 1564
1811 print CGI::table({class=>"FormLayout"}, 1565 print CGI::table({class=>"FormLayout"},
1812 CGI::Tr( 1566 CGI::Tr({},
1813 CGI::th({class=>"LeftHeader"}, "Course Name:"), 1567 CGI::th({class=>"LeftHeader"}, "Course Name:"),
1814 CGI::td( 1568 CGI::td(
1815 CGI::scrolling_list( 1569 CGI::scrolling_list(
1816 -name => "archive_courseID", 1570 -name => "archive_courseID",
1817 -values => \@courseIDs, 1571 -values => \@courseIDs,
1819 -size => 10, 1573 -size => 10,
1820 -multiple => 0, 1574 -multiple => 0,
1821 -labels => \%courseLabels, 1575 -labels => \%courseLabels,
1822 ), 1576 ),
1823 ), 1577 ),
1578
1824 ), 1579 ),
1580 CGI::Tr({},
1581 CGI::th({class=>"LeftHeader"}, "Delete course:"),
1582 CGI::td({-style=>'color:red'}, CGI::checkbox({
1583 -name=>'delete_course',
1584 -checked=>0,
1585 -value => 1,
1586 -label =>'Delete course after archiving. Caution there is no undo!',
1587 },
1588 ),
1589 ),
1590 )
1825 ); 1591 );
1826 1592
1827 print CGI::p( 1593 print CGI::p(
1828 "Currently the archive facility is only available for mysql databases. 1594 "Currently the archive facility is only available for mysql databases.
1829 It depends on the mysqldump application." 1595 It depends on the mysqldump application."
1830 ); 1596 );
1831# print CGI::p( 1597
1832# "If the course's database layout (indicated in parentheses above) is "
1833# . CGI::b("sql") . ", supply the SQL connections information requested below."
1834# );
1835 1598
1836# print CGI::start_table({class=>"FormLayout"});
1837# print CGI::Tr(CGI::td({colspan=>2},
1838# "Enter the user ID and password for an SQL account with sufficient permissions to archive an existing database."
1839# )
1840# );
1841# print CGI::Tr(
1842# CGI::th({class=>"LeftHeader"}, "SQL Admin Username:"),
1843# CGI::td(CGI::textfield("archive_sql_username", $archive_sql_username, 25)),
1844# );
1845# print CGI::Tr(
1846# CGI::th({class=>"LeftHeader"}, "SQL Admin Password:"),
1847# CGI::td(CGI::password_field("archive_sql_password", $archive_sql_password, 25)),
1848# );
1849#
1850# #print CGI::Tr(CGI::td({colspan=>2},
1851# # "The optionial SQL settings you enter below must match the settings in the DBI source"
1852# # . " specification " . CGI::tt($dbi_source) . ". Replace " . CGI::tt("COURSENAME")
1853# # . " with the course name you entered above."
1854# # )
1855# #);
1856# print CGI::Tr(
1857# CGI::th({class=>"LeftHeader"}, "SQL Server Host:"),
1858# CGI::td(
1859# CGI::textfield("archive_sql_host", $archive_sql_host, 25),
1860# CGI::br(),
1861# CGI::small("Leave blank to use the default host."),
1862# ),
1863# );
1864# print CGI::Tr(
1865# CGI::th({class=>"LeftHeader"}, "SQL Server Port:"),
1866# CGI::td(
1867# CGI::textfield("archive_sql_port", $archive_sql_port, 25),
1868# CGI::br(),
1869# CGI::small("Leave blank to use the default port."),
1870# ),
1871# );
1872#
1873# print CGI::Tr(
1874# CGI::th({class=>"LeftHeader"}, "SQL Database Name:"),
1875# CGI::td(
1876# CGI::textfield("archive_sql_database", $archive_sql_database, 25),
1877# CGI::br(),
1878# CGI::small("Leave blank to use the name ", CGI::tt("webwork_COURSENAME"), "."),
1879# ),
1880# );
1881# print CGI::end_table();
1882
1883 print CGI::p({style=>"text-align: center"}, CGI::submit("archive_course", "archive Course")); 1599 print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"archive_course", -value=>"archive Course"));
1884 1600
1885 print CGI::end_form(); 1601 print CGI::end_form();
1886} 1602}
1887 1603
1888sub archive_course_validate { 1604sub archive_course_validate {
1892 #my $db = $r->db; 1608 #my $db = $r->db;
1893 #my $authz = $r->authz; 1609 #my $authz = $r->authz;
1894 my $urlpath = $r->urlpath; 1610 my $urlpath = $r->urlpath;
1895 1611
1896 my $archive_courseID = $r->param("archive_courseID") || ""; 1612 my $archive_courseID = $r->param("archive_courseID") || "";
1897 my $archive_sql_host = $r->param("archive_sql_host") || "";
1898 my $archive_sql_port = $r->param("archive_sql_port") || "";
1899 my $archive_sql_username = $r->param("archive_sql_username") || "";
1900 my $archive_sql_password = $r->param("archive_sql_password") || "";
1901 my $archive_sql_database = $r->param("archive_sql_database") || "";
1902 1613
1903 my @errors; 1614 my @errors;
1904 1615
1905 if ($archive_courseID eq "") { 1616 if ($archive_courseID eq "") {
1906 push @errors, "You must specify a course name."; 1617 push @errors, "You must specify a course name.";
1907 } elsif ($archive_courseID eq $urlpath->arg("courseID")) { 1618 } elsif ($archive_courseID eq $urlpath->arg("courseID")) {
1908 push @errors, "You cannot archive the course you are currently using."; 1619 push @errors, "You cannot archive the course you are currently using.";
1909 } 1620 }
1621
1622 #my $ce2 = WeBWorK::CourseEnvironment->new(
1623 # $ce->{webworkDirs}->{root},
1624 # $ce->{webworkURLs}->{root},
1625 # $ce->{pg}->{directories}->{root},
1626 # $archive_courseID,
1627 #);
1628
1629 return @errors;
1630}
1631
1632sub archive_course_confirm {
1633 my ($self) = @_;
1634 my $r = $self->r;
1635 my $ce = $r->ce;
1636 #my $db = $r->db;
1637 #my $authz = $r->authz;
1638 #my $urlpath = $r->urlpath;
1639
1640 print CGI::h2("archive Course");
1641
1642 my $archive_courseID = $r->param("archive_courseID") || "";
1643 my $delete_course_flag = $r->param("delete_course") || "";
1910 1644
1911 my $ce2 = WeBWorK::CourseEnvironment->new( 1645 my $ce2 = WeBWorK::CourseEnvironment->new(
1912 $ce->{webworkDirs}->{root}, 1646 $ce->{webworkDirs}->{root},
1913 $ce->{webworkURLs}->{root}, 1647 $ce->{webworkURLs}->{root},
1914 $ce->{pg}->{directories}->{root}, 1648 $ce->{pg}->{directories}->{root},
1915 $archive_courseID, 1649 $archive_courseID,
1916 ); 1650 );
1917 1651
1918 if ($ce2->{dbLayoutName} eq "sql") { 1652 if ($ce2->{dbLayoutName} ) {
1919 push @errors, "You must specify the SQL admin username." if $archive_sql_username eq ""; 1653 print CGI::p("Are you sure you want to archive the course " . CGI::b($archive_courseID)
1920 #push @errors, "You must specify the SQL admin password." if $archive_sql_password eq ""; 1654 . "? ");
1921 #push @errors, "You must specify the SQL database name." if $archive_sql_database eq ""; 1655 print(CGI::p({-style=>'color:red; font-weight:bold'}, "Are you sure that you want to delete the course ".
1922 } 1656 CGI::b($archive_courseID). " after archiving? This cannot be undone!")) if $delete_course_flag;
1657
1923 1658
1924 return @errors; 1659 }
1660
1661 print CGI::start_form(-method=>"POST", -action=>$r->uri);
1662 print $self->hidden_authen_fields;
1663 print $self->hidden_fields("subDisplay");
1664 print $self->hidden_fields(qw/archive_courseID delete_course/);
1665
1666 print CGI::p({style=>"text-align: center"},
1667 CGI::submit(-name=>"decline_archive_course", -value=>"Don't archive"),
1668 "&nbsp;",
1669 CGI::submit(-name=>"confirm_archive_course", -value=>"archive"),
1670 );
1671
1672 print CGI::end_form();
1925} 1673}
1926 1674
1927sub archive_course_confirm { 1675sub do_archive_course {
1928 my ($self) = @_; 1676 my ($self) = @_;
1929 my $r = $self->r; 1677 my $r = $self->r;
1930 my $ce = $r->ce; 1678 my $ce = $r->ce;
1931 #my $db = $r->db; 1679 my $db = $r->db;
1932 #my $authz = $r->authz; 1680 #my $authz = $r->authz;
1933 #my $urlpath = $r->urlpath; 1681 #my $urlpath = $r->urlpath;
1934 1682
1935 print CGI::h2("archive Course");
1936
1937 my $archive_courseID = $r->param("archive_courseID") || ""; 1683 my $archive_courseID = $r->param("archive_courseID") || "";
1938 my $archive_sql_host = $r->param("archive_sql_host") || ""; 1684 my $delete_course_flag = $r->param("delete_course") || "";
1939 my $archive_sql_port = $r->param("archive_sql_port") || "";
1940 my $archive_sql_database = $r->param("archive_sql_database") || "";
1941 1685
1942 my $ce2 = WeBWorK::CourseEnvironment->new( 1686 my $ce2 = WeBWorK::CourseEnvironment->new(
1943 $ce->{webworkDirs}->{root}, 1687 $ce->{webworkDirs}->{root},
1944 $ce->{webworkURLs}->{root}, 1688 $ce->{webworkURLs}->{root},
1945 $ce->{pg}->{directories}->{root}, 1689 $ce->{pg}->{directories}->{root},
1946 $archive_courseID, 1690 $archive_courseID,
1947 ); 1691 );
1948 1692
1949 if ($ce2->{dbLayoutName} eq "sql") { 1693 # this is kinda left over from when we had 'gdbm' and 'sql' database layouts
1950 print CGI::p("Are you sure you want to archive the course " . CGI::b($archive_courseID) 1694 # below this line, we would grab values from getopt and put them in this hash
1951 . "? "); 1695 # but for now the hash can remain empty
1952
1953 print CGI::table({class=>"FormLayout"},
1954 CGI::Tr(
1955 CGI::th({class=>"LeftHeader"}, "SQL Server Host:"),
1956 CGI::td($archive_sql_host || "system default"),
1957 ),
1958 CGI::Tr(
1959 CGI::th({class=>"LeftHeader"}, "SQL Server Port:"),
1960 CGI::td($archive_sql_port || "system default"),
1961 ),
1962 CGI::Tr(
1963 CGI::th({class=>"LeftHeader"}, "SQL Database Name:"),
1964 CGI::td($archive_sql_database || "webwork_$archive_courseID"),
1965 ),
1966 );
1967 } else {
1968 print CGI::p("Are you sure you want to archive the course " . CGI::b($archive_courseID)
1969 . "? All course files and data will be destroyed. There is no undo available.");
1970 }
1971
1972 print CGI::start_form("POST", $r->uri);
1973 print $self->hidden_authen_fields;
1974 print $self->hidden_fields("subDisplay");
1975 print $self->hidden_fields(qw/archive_courseID archive_sql_host archive_sql_port archive_sql_username archive_sql_password archive_sql_database/);
1976
1977 print CGI::p({style=>"text-align: center"},
1978 CGI::submit("decline_archive_course", "Don't archive"),
1979 "&nbsp;",
1980 CGI::submit("confirm_archive_course", "archive"),
1981 );
1982
1983 print CGI::end_form();
1984}
1985
1986sub do_archive_course {
1987 my ($self) = @_;
1988 my $r = $self->r;
1989 my $ce = $r->ce;
1990 #my $db = $r->db;
1991 #my $authz = $r->authz;
1992 #my $urlpath = $r->urlpath;
1993
1994 my $archive_courseID = $r->param("archive_courseID") || "";
1995 my $archive_sql_host = $r->param("archive_sql_host") || "";
1996 my $archive_sql_port = $r->param("archive_sql_port") || "";
1997 my $archive_sql_username = $r->param("archive_sql_username") || "";
1998 my $archive_sql_password = $r->param("archive_sql_password") || "";
1999 my $archive_sql_database = $r->param("archive_sql_database") || "";
2000
2001 my $ce2 = WeBWorK::CourseEnvironment->new(
2002 $ce->{webworkDirs}->{root},
2003 $ce->{webworkURLs}->{root},
2004 $ce->{pg}->{directories}->{root},
2005 $archive_courseID,
2006 );
2007
2008 my %dbOptions; 1696 my %dbOptions;
2009 if ($ce2->{dbLayoutName} eq "sql") {
2010 $dbOptions{host} = $archive_sql_host if $archive_sql_host ne "";
2011 $dbOptions{port} = $archive_sql_port if $archive_sql_port ne "";
2012 $dbOptions{username} = $archive_sql_username;
2013 $dbOptions{password} = $archive_sql_password;
2014 $dbOptions{database} = $archive_sql_database || "webwork_$archive_courseID";
2015 }
2016 1697
2017 eval { 1698 eval {
2018 archiveCourse( 1699 archiveCourse(
2019 courseID => $archive_courseID, 1700 courseID => $archive_courseID,
2020 ce => $ce2, 1701 ce => $ce2,
2036 "\tarchived", 1717 "\tarchived",
2037 "", 1718 "",
2038 "", 1719 "",
2039 $archive_courseID, 1720 $archive_courseID,
2040 )); 1721 ));
1722
1723 if ($delete_course_flag) {
1724 eval {
1725 deleteCourse(
1726 courseID => $archive_courseID,
1727 ce => $ce2,
1728 dbOptions => \%dbOptions,
1729 );
1730 };
1731
1732 if ($@) {
1733 my $error = $@;
1734 print CGI::div({class=>"ResultsWithError"},
1735 CGI::p("An error occured while deleting the course $archive_courseID:"),
1736 CGI::tt(CGI::escapeHTML($error)),
1737 );
1738 } else {
1739 # mark the contact person in the admin course as dropped.
1740 # find the contact person for the course by searching the admin classlist.
1741 my @contacts = grep /_$archive_courseID$/, $db->listUsers;
1742 if (@contacts) {
1743 die "Incorrect number of contacts for the course $archive_courseID". join(" ", @contacts) if @contacts !=1;
1744 #warn "contacts", join(" ", @contacts);
1745 #my $composite_id = "${add_initial_userID}_${add_courseID}";
1746 my $composite_id = $contacts[0];
1747
1748 # mark the contact person as dropped.
1749 my $User = $db->getUser($composite_id);
1750 my $status_name = 'Drop';
1751 my $status_value = ($ce->status_name_to_abbrevs($status_name))[0];
1752 $User->status($status_value);
1753 $db->putUser($User);
1754 }
1755
1756 print CGI::div({class=>"ResultsWithoutError"},
1757 CGI::p("Successfully deleted the course $archive_courseID."),
1758 );
1759 }
1760
1761
1762 }
1763
1764# print CGI::start_form(-method=>"POST", -action=>$r->uri);
1765# print $self->hidden_authen_fields;
1766# print $self->hidden_fields("subDisplay");
1767#
1768# print CGI::p({style=>"text-align: center"}, CGI::submit("decline_archive_course", "OK"),);
1769#
1770# print CGI::end_form();
1771 }
1772}
1773##########################################################################
1774sub unarchive_course_form {
1775 my ($self) = @_;
1776 my $r = $self->r;
1777 my $ce = $r->ce;
1778 #my $db = $r->db;
1779 #my $authz = $r->authz;
1780 #my $urlpath = $r->urlpath;
1781
1782 my $unarchive_courseID = $r->param("unarchive_courseID") || "";
1783
1784 # First find courses which have been archived.
1785 my @courseIDs = listArchivedCourses($ce);
1786 @courseIDs = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive
1787
1788 my %courseLabels; # records... heh.
1789 foreach my $courseID (@courseIDs) {
1790 $courseLabels{$courseID} = $courseID;
1791 }
1792
1793 print CGI::h2("Unarchive Course -- not yet operational");
1794
2041 print CGI::start_form("POST", $r->uri); 1795 print CGI::start_form(-method=>"POST", -action=>$r->uri);
1796 print $self->hidden_authen_fields;
1797 print $self->hidden_fields("subDisplay");
1798
1799 print CGI::p("Select a course to unarchive.");
1800
1801 print CGI::table({class=>"FormLayout"},
1802 CGI::Tr({},
1803 CGI::th({class=>"LeftHeader"}, "Course Name:"),
1804 CGI::td(
1805 CGI::scrolling_list(
1806 -name => "unarchive_courseID",
1807 -values => \@courseIDs,
1808 -default => $unarchive_courseID,
1809 -size => 10,
1810 -multiple => 0,
1811 -labels => \%courseLabels,
1812 ),
1813 ),
1814 ),
1815 );
1816
1817 print CGI::p(
1818 "Currently the unarchive facility is only available for mysql databases.
1819 It depends on the mysqldump application."
1820 );
1821
1822
1823 print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"unarchive_course", -value=>"Unarchive Course"));
1824
1825 print CGI::end_form();
1826}
1827
1828sub unarchive_course_validate {
1829 my ($self) = @_;
1830 my $r = $self->r;
1831 my $ce = $r->ce;
1832 #my $db = $r->db;
1833 #my $authz = $r->authz;
1834 my $urlpath = $r->urlpath;
1835
1836 my $unarchive_courseID = $r->param("unarchive_courseID") || "";
1837
1838 my @errors;
1839
1840 my $new_courseID = $unarchive_courseID; $new_courseID =~ s/\.tar\.gz$//;
1841
1842 if ($new_courseID eq "") {
1843 push @errors, "You must specify a course name.";
1844 } elsif ( -d $ce->{webworkDirs}->{courses}."/$new_courseID" ) {
1845 #Check that a directory for this course doesn't already exist
1846 push @errors, "A directory already exists with the name $new_courseID.
1847 You must first delete this existing course before you can unarchive.";
1848 }
1849
1850
1851
1852 return @errors;
1853}
1854
1855sub unarchive_course_confirm {
1856 my ($self) = @_;
1857 my $r = $self->r;
1858 my $ce = $r->ce;
1859 #my $db = $r->db;
1860 #my $authz = $r->authz;
1861 #my $urlpath = $r->urlpath;
1862
1863 print CGI::h2("Unarchive Course");
1864
1865 my $unarchive_courseID = $r->param("unarchive_courseID") || "";
1866
1867 my $new_courseID = $unarchive_courseID; $new_courseID =~ s/\.tar\.gz$//;
1868
1869
1870
1871 print CGI::start_form(-method=>"POST", -action=>$r->uri);
1872 print CGI::p($unarchive_courseID," to course ",
1873 CGI::input({-name=>'new_courseID', -value=>$new_courseID})
1874 );
1875
1876 print $self->hidden_authen_fields;
1877 print $self->hidden_fields("subDisplay");
1878 print $self->hidden_fields(qw/unarchive_courseID/);
1879
1880 print CGI::p({style=>"text-align: center"},
1881 CGI::submit(-name=>"decline_unarchive_course", -value=>"Don't unarchive"),
1882 "&nbsp;",
1883 CGI::submit(-name=>"confirm_unarchive_course", -value=>"unarchive"),
1884 );
1885
1886 print CGI::end_form();
1887}
1888
1889sub do_unarchive_course {
1890 my ($self) = @_;
1891 my $r = $self->r;
1892 my $ce = $r->ce;
1893 #my $db = $r->db;
1894 #my $authz = $r->authz;
1895 my $urlpath = $r->urlpath;
1896 my $new_courseID = $r->param("new_courseID") || "";
1897 my $unarchive_courseID = $r->param("unarchive_courseID") || "";
1898
1899 my %dbOptions;
1900
1901 eval {
1902 unarchiveCourse(
1903 courseID => $new_courseID,
1904 archivePath =>$ce->{webworkDirs}->{courses}."/$unarchive_courseID",
1905 ce => $ce , # $ce2,
1906 dbOptions => undef,
1907 );
1908 };
1909
1910 if ($@) {
1911 my $error = $@;
1912 print CGI::div({class=>"ResultsWithError"},
1913 CGI::p("An error occured while archiving the course $unarchive_courseID:"),
1914 CGI::tt(CGI::escapeHTML($error)),
1915 );
1916 } else {
1917 print CGI::div({class=>"ResultsWithoutError"},
1918 CGI::p("Successfully unarchived $unarchive_courseID to the course $new_courseID"),
1919 );
1920 writeLog($ce, "hosted_courses", join("\t",
1921 "\tunarchived",
1922 "",
1923 "",
1924 "$unarchive_courseID to $new_courseID",
1925 ));
1926
1927 my $newCoursePath = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets",
1928 courseID => $new_courseID);
1929 my $newCourseURL = $self->systemLink($newCoursePath, authen => 0);
1930 print CGI::div({style=>"text-align: center"},
1931 CGI::a({href=>$newCourseURL}, "Log into $new_courseID"),
1932 );
1933 }
1934}
1935
1936################################################################################
1937## location management routines; added by DG [Danny Ginn] 20070215
1938## revised by glarose
1939
1940sub 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({}, ["&nbsp;", "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({},"&nbsp;"),
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({}, "&nbsp;"),
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=>["",
2028 "selected_locations",
2029 @locationIDs],
2030 -labels=>{selected_locations => "locations selected below",
2031 "" => "no location"}) .
2032 CGI::span({-style=>"color:#C33;"}, " Confirm: ") .
2033 CGI::checkbox({-name=>"delete_confirm",
2034 -value=>"true",
2035 -label=>""}) ) );
2036 print CGI::end_table();
2037
2038 print CGI::p({}, CGI::submit(-name=>"manage_locations", -value=>"Take Action!"));
2039
2040 # existing location table
2041 # FIXME: the styles for this table should be off in a stylesheet
2042 # somewhere
2043 print CGI::start_div({align=>"center"}),
2044 CGI::start_table({border=>1, cellpadding=>2});
2045 print CGI::Tr({style=>"background-color:#e0e0e0;font-size:92%", align=>"left"},
2046 CGI::th({}, ["Select", "Location", "Description",
2047 "Addresses"]));
2048 foreach my $loc ( @locations ) {
2049 my $editAddr = $self->systemLink($urlpath, params=>{subDisplay=>"manage_locations", manage_location_action=>"edit_location_form", edit_location=>$loc->location_id});
2050 print CGI::Tr({valign=>'top',style=>"background-color:#eeeeee;"},
2051 CGI::td({style=>'font-size:85%;'},
2052 [ CGI::checkbox(-name=>"delete_selected",
2053 -value=>$loc->location_id,
2054 -label=>''),
2055 CGI::a({href=>$editAddr}, $loc->location_id),
2056 $loc->description,
2057 join(', ', @{$locAddr{$loc->location_id}}) ]));
2058 }
2059 print CGI::end_table(), CGI::end_div();
2060 print CGI::end_form();
2061
2062
2063}
2064
2065sub add_location_handler {
2066 my $self = shift();
2067 my $r = $self->r;
2068 my $db = $r->db;
2069
2070 # the location data we're to add
2071 my $locationID = $r->param("new_location_name");
2072 my $locationDescr = $r->param("new_location_description");
2073 my $locationAddr = $r->param("new_location_addresses");
2074 # break the addresses up
2075 $locationAddr =~ s/\s*-\s*/-/g;
2076 $locationAddr =~ s/\s*\/\s*/\//g;
2077 my @addresses = split(/\s+/, $locationAddr);
2078
2079 # sanity checks
2080 my $badAddr = '';
2081 foreach my $addr ( @addresses ) {
2082 unless ( new Net::IP($addr) ) {
2083 $badAddr .= "$addr, ";
2084 $locationAddr =~ s/$addr\n//s;
2085 }
2086 }
2087 $badAddr =~ s/, $//;
2088
2089 # a check to be sure that the location addresses don't already
2090 # exist
2091 my $badLocAddr;
2092 if ( ! $badAddr && $locationID ) {
2093 if ( $db->countLocationAddresses( $locationID ) ) {
2094 my @allLocAddr = $db->listLocationAddresses($locationID);
2095 foreach my $addr ( @addresses ) {
2096 $badLocAddr .= "$addr, "
2097 if ( grep {/^$addr$/} @allLocAddr );
2098 }
2099 $badLocAddr =~ s/, $//;
2100 }
2101 }
2102
2103 if ( ! @addresses || ! $locationID || ! $locationDescr ) {
2104 print CGI::div({-class=>"ResultsWithError"},
2105 "Missing required input data. Please check " .
2106 "that you have filled in all of the create " .
2107 "location fields and resubmit.");
2108 } elsif ( $badAddr ) {
2109 $r->param("new_location_addresses", $locationAddr);
2110 print CGI::div({-class=>"ResultsWithError"},
2111 "Address(es) $badAddr is(are) not in a " .
2112 "recognized form. Please check your " .
2113 "data entry and resubmit.");
2114 } elsif ( $db->existsLocation( $locationID ) ) {
2115 print CGI::div({-class=>"ResultsWithError"},
2116 "A location with the name $locationID " .
2117 "already exists in the database. Did " .
2118 "you mean to edit that location instead?");
2119 } elsif ( $badLocAddr ) {
2120 print CGI::div({-class=>"ResultsWithError"},
2121 "Address(es) $badLocAddr already exist " .
2122 "in the database. THIS SHOULD NOT HAPPEN! " .
2123 "Please double check the integrity of " .
2124 "the WeBWorK database before continuing.");
2125 } else {
2126 # add the location
2127 my $locationObj = $db->newLocation;
2128 $locationObj->location_id( $locationID );
2129 $locationObj->description( $locationDescr );
2130 $db->addLocation( $locationObj );
2131
2132 # and add the addresses
2133 foreach my $addr ( @addresses ) {
2134 my $locationAddress = $db->newLocationAddress;
2135 $locationAddress->location_id($locationID);
2136 $locationAddress->ip_mask($addr);
2137
2138 $db->addLocationAddress( $locationAddress );
2139 }
2140
2141 # we've added the location, so clear those param
2142 # entries
2143 $r->param('manage_location_action','none');
2144 $r->param('new_location_name','');
2145 $r->param('new_location_description','');
2146 $r->param('new_location_addresses','');
2147
2148 print CGI::div({-class=>"ResultsWithoutError"},
2149 "Location $locationID has been created, " .
2150 "with addresses " . join(', ', @addresses) .
2151 ".");
2152 }
2153
2154 $self->manage_location_form;
2155}
2156
2157sub delete_location_handler {
2158 my $self = shift;
2159 my $r = $self->r;
2160 my $db = $r->db;
2161
2162 # what location are we deleting?
2163 my $locationID = $r->param("delete_location");
2164 # check for selected deletions if appropriate
2165 my @delLocations = ( $locationID );
2166 if ( $locationID eq 'selected_locations' ) {
2167 @delLocations = $r->param("delete_selected");
2168 $locationID = @delLocations;
2169 }
2170 # are we sure?
2171 my $confirm = $r->param("delete_confirm");
2172
2173 my $badID;
2174 if ( ! $locationID ) {
2175 print CGI::div({-class=>"ResultsWithError"},
2176 "Please provide a location name " .
2177 "to delete.");
2178
2179 } elsif ( $badID = $self->existsLocations_helper( @delLocations ) ) {
2180 print CGI::div({-class=>"ResultsWithError"},
2181 "No location with name $badID " .
2182 "exists in the database.");
2183
2184 } elsif ( ! $confirm || $confirm ne 'true' ) {
2185 print CGI::div({-class=>"ResultsWithError"},
2186 "Location deletion requires confirmation.");
2187 } else {
2188 foreach ( @delLocations ) {
2189 $db->deleteLocation( $_ );
2190 }
2191 print CGI::div({-class=>"ResultsWithoutError"},
2192 "Location" . (@delLocations > 1 ? 's ' : ' ') .
2193 join(', ', @delLocations) .
2194 (@delLocations > 1 ? ' have ' : ' has ' ) .
2195 'been deleted.');
2196 $r->param('manage_location_action','none');
2197 $r->param('delete_location','');
2198 }
2199 $self->manage_location_form;
2200}
2201sub existsLocations_helper {
2202 my ($self, @locations) = @_;
2203 my $db = $self->r->db;
2204 foreach ( @locations ) {
2205 return $_ if ( ! $db->existsLocation($_) );
2206 }
2207 return 0;
2208}
2209
2210sub edit_location_form {
2211 my $self = shift;
2212 my $r = $self->r;
2213 my $db = $r->db;
2214
2215 my $locationID = $r->param("edit_location");
2216 if ( $db->existsLocation( $locationID ) ) {
2217 my $location = $db->getLocation($locationID);
2218 # this doesn't give that nice a sort for IP addresses,
2219 # b/c there's the problem with 192.168.1.168 sorting
2220 # ahead of 192.168.1.2. we could do better if we
2221 # either invoked Net::IP in the sort routine, or if
2222 # we insisted on dealing only with IPv4. rather than
2223 # deal with either of those, we'll leave this for now
2224 my @locAddresses = sort { $a cmp $b }
2225 $db->listLocationAddresses($locationID);
2226
2227 print CGI::h2("Editing location ", $locationID);
2228
2229 print CGI::p({},"Edit the current value of the location ",
2230 "description, if desired, then add and select ",
2231 "addresses to delete, and then click the ",
2232 "\"Take Action\" button to make all of your ",
2233 "changes. Or, click \"Manage Locations\" ",
2234 "above to make no changes and return to the ",
2235 "Manage Locations page.");
2236
2237 print CGI::start_form(-method=>"POST",
2238 -action=>$r->uri);
2042 print $self->hidden_authen_fields; 2239 print $self->hidden_authen_fields;
2043 print $self->hidden_fields("subDisplay"); 2240 print $self->hidden_fields("subDisplay");
2044 2241 print CGI::hidden(-name=>'edit_location',
2045 print CGI::p({style=>"text-align: center"}, CGI::submit("decline_archive_course", "OK"),); 2242 -default=>$locationID);
2046 2243 print CGI::hidden(-name=>'manage_location_action',
2244 -default=>'edit_location_handler');
2245
2246 print CGI::start_table();
2247 print CGI::Tr({-valign=>'top'},
2248 CGI::td({-colspan=>3},
2249 "Location description: ", CGI::br(),
2250 CGI::textfield(-name=>"location_description",
2251 -size=>"50",
2252 -default=>$location->description)));
2253 print CGI::Tr({-valign=>'top'},
2254 CGI::td({-width=>"50%"},
2255 "Addresses to add to the location " .
2256 "(enter one per line, as single IP addresses " .
2257 "(e.g., 192.168.1.101), address masks " .
2258 "(e.g., 192.168.1.0/24), or IP ranges " .
2259 "(e.g., 192.168.1.101-192.168.1.150)):" .
2260 CGI::br() .
2261 CGI::textarea({-name=>"new_location_addresses",
2262 -rows=>5, -columns=>28})),
2263 CGI::td({}, "&nbsp;"),
2264 CGI::td({-width=>"50%"},
2265 "Existing addresses for the location are " .
2266 "given in the scrolling list below. Select " .
2267 "addresses from the list to delete them:" .
2268 CGI::br() .
2269 CGI::scrolling_list(-name=>'delete_location_addresses',
2270 -values=>[@locAddresses],
2271 -size=>8,
2272 -multiple=>'multiple') .
2273 CGI::br() . "or: " .
2274 CGI::checkbox(-name=>'delete_all_addresses',
2275 -value=>'true',
2276 -label=>'Delete all existing addresses')
2277 ));
2278
2047 print CGI::end_form(); 2279 print CGI::end_table();
2280
2281 print CGI::p({},CGI::submit(-value=>'Take Action!'));
2282
2283 } else {
2284 print CGI::div({-class=>"ResultsWithError"},
2285 "Location $locationID does not exist " .
2286 "in the WeBWorK database. Please check " .
2287 "your input (perhaps you need to reload " .
2288 "the location management page?).");
2289
2290 $self->manage_location_form;
2291 }
2292}
2293
2294sub edit_location_handler {
2295 my $self = shift;
2296 my $r = $self->r;
2297 my $db = $r->db;
2298
2299 my $locationID = $r->param("edit_location");
2300 my $locationDesc = $r->param("location_description");
2301 my $addAddresses = $r->param("new_location_addresses");
2302 my @delAddresses = $r->param("delete_location_addresses");
2303 my $deleteAll = $r->param("delete_all_addresses");
2304
2305 # gut check
2306 if ( ! $locationID ) {
2307 print CGI::div({-class=>"ResultsWithError"},
2308 "No location specified to edit?! " .
2309 "Please check your input data.");
2310 $self->manage_location_form;
2311
2312 } elsif ( ! $db->existsLocation( $locationID ) ) {
2313 print CGI::div({-class=>"ResultsWithError"},
2314 "Location $locationID does not exist " .
2315 "in the WeBWorK database. Please check " .
2316 "your input (perhaps you need to reload " .
2317 "the location management page?).");
2318 $self->manage_location_form;
2319 } else {
2320 my $location = $db->getLocation($locationID);
2321
2322 # get the current location addresses. if we're deleting
2323 # all of the existing addresses, we don't use this list
2324 # to determine which addresses to add, however.
2325 my @currentAddr = $db->listLocationAddresses($locationID);
2326 my @compareAddr = ( ! $deleteAll || $deleteAll ne 'true' )
2327 ? @currentAddr : ();
2328
2329 my $doneMsg = '';
2330
2331 if ($locationDesc && $location->description ne $locationDesc) {
2332 $location->description($locationDesc);
2333 $db->putLocation($location);
2334 $doneMsg .= CGI::p({},"Updated location description.");
2335 }
2336 # get the actual addresses to add out of the text field
2337 $addAddresses =~ s/\s*-\s*/-/g;
2338 $addAddresses =~ s/\s*\/\s*/\//g;
2339 my @addAddresses = split(/\s+/, $addAddresses);
2340
2341 # make sure that we're adding and deleting only those
2342 # addresses that are not yet/currently in the location
2343 # addresses
2344 my @toAdd = (); my @noAdd = ();
2345 my @toDel = (); my @noDel = ();
2346 foreach my $addr ( @addAddresses ) {
2347 if (grep {/^$addr$/} @compareAddr) {push(@noAdd,$addr);}
2348 else { push(@toAdd, $addr); }
2349 }
2350 if ( $deleteAll && $deleteAll eq 'true' ) {
2351 @toDel = @currentAddr;
2352 } else {
2353 foreach my $addr ( @delAddresses ) {
2354 if (grep {/^$addr$/} @currentAddr) {
2355 push(@toDel,$addr);
2356 } else { push(@noDel, $addr); }
2357 }
2358 }
2359
2360 # and make sure that all of the addresses we're adding are
2361 # a sensible form
2362 my $badAddr = '';
2363 foreach my $addr ( @toAdd ) {
2364 unless ( new Net::IP($addr) ) {
2365 $badAddr .= "$addr, ";
2366 }
2367 }
2368 $badAddr =~ s/, $//;
2369
2370 # delete addresses first, because we allow deletion of
2371 # all existing addresses, then addition of addresses.
2372 # note that we don't allow deletion and then addition
2373 # of the same address normally, however; in that case
2374 # we'll end up just deleting the address.
2375 foreach ( @toDel ) {
2376 $db->deleteLocationAddress($locationID, $_);
2377 }
2378 foreach ( @toAdd ) {
2379 my $locAddr = $db->newLocationAddress;
2380 $locAddr->location_id($locationID);
2381 $locAddr->ip_mask($_);
2382
2383 $db->addLocationAddress($locAddr);
2384 }
2385
2386 my $addrMsg = '';
2387 $addrMsg .= "Deleted addresses " . join(', ', @toDel) .
2388 " from location." . CGI::br() if ( @toDel );
2389 $addrMsg .= "Added addresses " . join(', ', @toAdd) .
2390 " to location $locationID. " if ( @toAdd );
2391
2392 my $badMsg = '';
2393 $badMsg .= 'Address(es) ' . join(', ', @noAdd) .
2394 " in the add list is(are) already in the " .
2395 "location $locationID, and so were " .
2396 "skipped." . CGI::br() if ( @noAdd );
2397 $badMsg .= "Address(es) $badAddr is(are) not in a " .
2398 "recognized form. Please check your data " .
2399 "entry and try again." . CGI::br() if ( $badAddr );
2400 $badMsg .= 'Address(es) ' . join(', ', @noDel) .
2401 " in the delete list is(are) not in the " .
2402 "location $locationID, and so were " .
2403 "skipped." if ( @noDel );
2404
2405 print CGI::div({-class=>"ResultsWithError"}, $badMsg)
2406 if ( $badMsg );
2407 if ( $doneMsg || $addrMsg ) {
2408 print CGI::div({-class=>"ResultsWithoutError"},
2409 CGI::p({}, $doneMsg, $addrMsg));
2410 } else {
2411 print CGI::div({-class=>"ResultsWithError"},
2412 "No valid changes submitted for ",
2413 "location $locationID.");
2414 }
2415
2416 $self->edit_location_form;
2048 } 2417 }
2049} 2418}
2050 2419
2051################################################################################ 2420################################################################################
20521; 24211;

Legend:
Removed from v.3621  
changed lines
  Added in v.4918

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9