Parent Directory
|
Revision Log
HEAD merge: a couple of things. - sql info fields in delete form matches those in add form - use user_id instead of email_address for PRINT_FILE_NAMES_FOR
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v 1.24 2004/07/10 16:28:56 sh002i Exp $ 5 # 6 # This program is free software; you can redistribute it and/or modify it under 7 # the terms of either: (a) the GNU General Public License as published by the 8 # Free Software Foundation; either version 2, or (at your option) any later 9 # version, or (b) the "Artistic License" which comes with this package. 10 # 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 14 # Artistic License for more details. 15 ################################################################################ 16 17 package WeBWorK::ContentGenerator::CourseAdmin; 18 use base qw(WeBWorK::ContentGenerator); 19 20 =head1 NAME 21 22 WeBWorK::ContentGenerator::CourseAdmin - Add, rename, and delete courses. 23 24 =cut 25 26 use strict; 27 use warnings; 28 use CGI::Pretty qw(); 29 use Data::Dumper; 30 use File::Temp qw/tempfile/; 31 use WeBWorK::CourseEnvironment; 32 use WeBWorK::Utils qw(cryptPassword writeLog); 33 use WeBWorK::Utils::CourseManagement qw(addCourse deleteCourse listCourses); 34 use WeBWorK::Utils::DBImportExport qw(dbExport dbImport); 35 36 sub pre_header_initialize { 37 my ($self) = @_; 38 my $r = $self->r; 39 my $ce = $r->ce; 40 my $db = $r->db; 41 my $authz = $r->authz; 42 my $urlpath = $r->urlpath; 43 my $user = $r->param('user'); 44 45 # check permissions 46 unless ($authz->hasPermissions($user, "create_and_delete_courses")) { 47 $self->addmessage( CGI::div({class=>'ResultsWithError'},"$user is not authorized to create or delete courses") ); 48 return; 49 } 50 51 ## if the user is asking for the downloaded database... 52 #if (defined $r->param("download_exported_database")) { 53 # my $courseID = $r->param("export_courseID"); 54 # my $random_chars = $r->param("download_exported_database"); 55 # 56 # die "courseID not specified" unless defined $courseID; 57 # die "invalid file specification" unless $random_chars =~ m/^\w+$/; 58 # 59 # my $tempdir = $ce->{webworkDirs}->{tmp}; 60 # my $export_file = "$tempdir/db_export_$random_chars"; 61 # 62 # $self->reply_with_file("application/xml", $export_file, "${courseID}_database.xml", 0); 63 # 64 # return ""; 65 #} 66 # 67 ## otherwise... 68 69 my @errors; 70 my $method_to_call; 71 72 my $subDisplay = $r->param("subDisplay"); 73 if (defined $subDisplay) { 74 75 if ($subDisplay eq "add_course") { 76 if (defined $r->param("add_course")) { 77 @errors = $self->add_course_validate; 78 if (@errors) { 79 $method_to_call = "add_course_form"; 80 } else { 81 $method_to_call = "do_add_course"; 82 } 83 } else { 84 $method_to_call = "add_course_form"; 85 } 86 } 87 88 elsif ($subDisplay eq "delete_course") { 89 if (defined $r->param("delete_course")) { 90 # validate or confirm 91 @errors = $self->delete_course_validate; 92 if (@errors) { 93 $method_to_call = "delete_course_form"; 94 } else { 95 $method_to_call = "delete_course_confirm"; 96 } 97 } elsif (defined $r->param("confirm_delete_course")) { 98 # validate and delete 99 @errors = $self->delete_course_validate; 100 if (@errors) { 101 $method_to_call = "delete_course_form"; 102 } else { 103 $method_to_call = "do_delete_course"; 104 } 105 } else { 106 # form only 107 $method_to_call = "delete_course_form"; 108 } 109 } 110 111 elsif ($subDisplay eq "export_database") { 112 if (defined $r->param("export_database")) { 113 @errors = $self->export_database_validate; 114 if (@errors) { 115 $method_to_call = "export_database_form"; 116 } else { 117 # we have to do something special here, since we're sending 118 # the database as we export it. $method_to_call still gets 119 # set here, but it gets caught by header() and content() 120 # below instead of by body(). 121 $method_to_call = "do_export_database"; 122 } 123 } else { 124 $method_to_call = "export_database_form"; 125 } 126 } 127 128 elsif ($subDisplay eq "import_database") { 129 if (defined $r->param("import_database")) { 130 @errors = $self->import_database_validate; 131 if (@errors) { 132 $method_to_call = "import_database_form"; 133 } else { 134 $method_to_call = "do_import_database"; 135 } 136 } else { 137 $method_to_call = "import_database_form"; 138 } 139 } 140 141 else { 142 @errors = "Unrecognized sub-display @{[ CGI::b($subDisplay) ]}."; 143 } 144 145 } 146 147 $self->{errors} = \@errors; 148 $self->{method_to_call} = $method_to_call; 149 } 150 151 sub header { 152 my ($self) = @_; 153 my $method_to_call = $self->{method_to_call}; 154 if (defined $method_to_call and $method_to_call eq "do_export_database") { 155 my $r = $self->r; 156 my $courseID = $r->param("export_courseID"); 157 $r->content_type("application/octet-stream"); 158 $r->header_out("Content-Disposition" => "attachment; filename=\"${courseID}_database.xml\""); 159 $r->send_http_header; 160 } else { 161 $self->SUPER::header; 162 } 163 } 164 165 # sends: 166 # 167 # HTTP/1.1 200 OK 168 # Date: Fri, 09 Jul 2004 19:05:55 GMT 169 # Server: Apache/1.3.27 (Unix) mod_perl/1.27 170 # Content-Disposition: attachment; filename="mth143_database.xml" 171 # Connection: close 172 # Content-Type: application/octet-stream 173 174 sub content { 175 my ($self) = @_; 176 my $method_to_call = $self->{method_to_call}; 177 if (defined $method_to_call and $method_to_call eq "do_export_database") { 178 print "<!-- ؆˸͖۵ -->\n"; 179 print "<!-- Those were some high-bit characters to convince Safari that we really do want this saved as a file. -->\n"; 180 $self->do_export_database; 181 } else { 182 $self->SUPER::content; 183 } 184 } 185 186 sub body { 187 my ($self) = @_; 188 my $r = $self->r; 189 my $ce = $r->ce; 190 my $db = $r->db; 191 my $authz = $r->authz; 192 my $urlpath = $r->urlpath; 193 194 my $user = $r->param('user'); 195 196 # check permissions 197 unless ($authz->hasPermissions($user, "create_and_delete_courses")) { 198 return ""; 199 } 200 201 print CGI::p({style=>"text-align: center"}, 202 CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"add_course"})}, "Add Course"), 203 " | ", 204 CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"delete_course"})}, "Delete Course"), 205 " | ", 206 CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"export_database"})}, "Export Database"), 207 " | ", 208 CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"import_database"})}, "Import Database"), 209 ); 210 211 print CGI::hr(); 212 213 my @errors = @{$self->{errors}}; 214 my $method_to_call = $self->{method_to_call}; 215 216 if (@errors) { 217 print CGI::div({class=>"ResultsWithError"}, 218 CGI::p("Please correct the following errors and try again:"), 219 CGI::ul(CGI::li(\@errors)), 220 ); 221 } 222 223 if (defined $method_to_call and $method_to_call ne "") { 224 $self->$method_to_call; 225 } 226 227 return ""; 228 } 229 230 ################################################################################ 231 232 sub add_course_form { 233 my ($self) = @_; 234 my $r = $self->r; 235 my $ce = $r->ce; 236 #my $db = $r->db; 237 #my $authz = $r->authz; 238 #my $urlpath = $r->urlpath; 239 240 my $add_courseID = $r->param("add_courseID") || ""; 241 my $add_courseTitle = $r->param("add_courseTitle") || ""; 242 my $add_courseInstitution = $r->param("add_courseInstitution") || ""; 243 244 my $add_admin_users = $r->param("add_admin_users") || ""; 245 246 my $add_initial_userID = $r->param("add_initial_userID") || ""; 247 my $add_initial_password = $r->param("add_initial_password") || ""; 248 my $add_initial_confirmPassword = $r->param("add_initial_confirmPassword") || ""; 249 my $add_initial_firstName = $r->param("add_initial_firstName") || ""; 250 my $add_initial_lastName = $r->param("add_initial_lastName") || ""; 251 my $add_initial_email = $r->param("add_initial_email") || ""; 252 253 my $add_templates_course = $r->param("add_templates_course") || ""; 254 255 my $add_dbLayout = $r->param("add_dbLayout") || ""; 256 my $add_sql_host = $r->param("add_sql_host") || ""; 257 my $add_sql_port = $r->param("add_sql_port") || ""; 258 my $add_sql_username = $r->param("add_sql_username") || ""; 259 my $add_sql_password = $r->param("add_sql_password") || ""; 260 my $add_sql_database = $r->param("add_sql_database") || ""; 261 my $add_sql_wwhost = $r->param("add_sql_wwhost") || ""; 262 my $add_gdbm_globalUserID = $r->param("add_gdbm_globalUserID") || ""; 263 264 my @dbLayouts = sort keys %{ $ce->{dbLayouts} }; 265 266 my $ce2 = WeBWorK::CourseEnvironment->new( 267 $ce->{webworkDirs}->{root}, 268 $ce->{webworkURLs}->{root}, 269 $ce->{pg}->{directories}->{root}, 270 "COURSENAME", 271 ); 272 273 my $dbi_source = do { 274 # find the most common SQL source (stolen from CourseManagement.pm) 275 my %sources; 276 foreach my $table (keys %{ $ce2->{dbLayouts}->{sql} }) { 277 $sources{$ce2->{dbLayouts}->{sql}->{$table}->{source}}++; 278 } 279 my $source; 280 if (keys %sources > 1) { 281 foreach my $curr (keys %sources) { 282 $source = $curr if not defined $source or 283 $sources{$curr} > $sources{$source}; 284 } 285 } else { 286 ($source) = keys %sources; 287 } 288 $source; 289 }; 290 291 my @existingCourses = listCourses($ce); 292 @existingCourses = sort @existingCourses; 293 294 print CGI::h2("Add Course"); 295 296 print CGI::start_form("POST", $r->uri); 297 print $self->hidden_authen_fields; 298 print $self->hidden_fields("subDisplay"); 299 300 print CGI::p("Specify an ID, title, and institution for the new course. The course ID may contain only letters, numbers, hyphens, and underscores."); 301 302 print CGI::table({class=>"FormLayout"}, 303 CGI::Tr( 304 CGI::th({class=>"LeftHeader"}, "Course ID:"), 305 CGI::td(CGI::textfield("add_courseID", $add_courseID, 25)), 306 ), 307 CGI::Tr( 308 CGI::th({class=>"LeftHeader"}, "Course Title:"), 309 CGI::td(CGI::textfield("add_courseTitle", $add_courseTitle, 25)), 310 ), 311 CGI::Tr( 312 CGI::th({class=>"LeftHeader"}, "Institution:"), 313 CGI::td(CGI::textfield("add_courseInstitution", $add_courseInstitution, 25)), 314 ), 315 ); 316 317 print CGI::p("To add the WeBWorK administrators to the new course (as instructors) check the box below."); 318 319 print CGI::p(CGI::checkbox("add_admin_users", $add_admin_users, "on", "Add WeBWorK administrators to new course")); 320 321 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."); 322 323 print CGI::table({class=>"FormLayout"}, CGI::Tr( 324 CGI::td( 325 CGI::table({class=>"FormLayout"}, 326 CGI::Tr( 327 CGI::th({class=>"LeftHeader"}, "User ID:"), 328 CGI::td(CGI::textfield("add_initial_userID", $add_initial_userID, 25)), 329 ), 330 CGI::Tr( 331 CGI::th({class=>"LeftHeader"}, "Password:"), 332 CGI::td(CGI::password_field("add_initial_password", $add_initial_password, 25)), 333 ), 334 CGI::Tr( 335 CGI::th({class=>"LeftHeader"}, "Confirm Password:"), 336 CGI::td(CGI::password_field("add_initial_confirmPassword", $add_initial_confirmPassword, 25)), 337 ), 338 ), 339 ), 340 CGI::td( 341 CGI::table({class=>"FormLayout"}, 342 CGI::Tr( 343 CGI::th({class=>"LeftHeader"}, "First Name:"), 344 CGI::td(CGI::textfield("add_initial_firstName", $add_initial_firstName, 25)), 345 ), 346 CGI::Tr( 347 CGI::th({class=>"LeftHeader"}, "Last Name:"), 348 CGI::td(CGI::textfield("add_initial_lastName", $add_initial_lastName, 25)), 349 ), 350 CGI::Tr( 351 CGI::th({class=>"LeftHeader"}, "Email Address:"), 352 CGI::td(CGI::textfield("add_initial_email", $add_initial_email, 25)), 353 ), 354 ), 355 356 ), 357 )); 358 359 print CGI::p("To copy problem templates from an existing course, select the course below."); 360 361 print CGI::table({class=>"FormLayout"}, 362 CGI::Tr( 363 CGI::th({class=>"LeftHeader"}, "Copy templates from:"), 364 CGI::td( 365 CGI::popup_menu( 366 -name => "add_templates_course", 367 -values => [ "", @existingCourses ], 368 -default => $add_templates_course, 369 #-size => 10, 370 #-multiple => 0, 371 #-labels => \%courseLabels, 372 ), 373 374 ), 375 ), 376 ); 377 378 print CGI::p("Select a database layout below."); 379 380 foreach my $dbLayout (@dbLayouts) { 381 print CGI::start_table({class=>"FormLayout"}); 382 383 # we generate singleton radio button tags ourselves because it's too much of a pain to do it with CGI.pm 384 print CGI::Tr( 385 CGI::td({style=>"text-align: right"}, 386 '<input type="radio" name="add_dbLayout" value="' . $dbLayout . '"' 387 . ($add_dbLayout eq $dbLayout ? " checked" : "") . ' />', 388 ), 389 CGI::td($dbLayout), 390 ); 391 392 print CGI::start_Tr(); 393 print CGI::td(); # for indentation :( 394 print CGI::start_td(); 395 396 if ($dbLayout eq "sql") { 397 print CGI::start_table({class=>"FormLayout"}); 398 print CGI::Tr(CGI::td({colspan=>2}, 399 "Enter the user ID and password for an SQL account with sufficient permissions to create a new database." 400 ) 401 ); 402 print CGI::Tr( 403 CGI::th({class=>"LeftHeader"}, "SQL Admin Username:"), 404 CGI::td(CGI::textfield("add_sql_username", $add_sql_username, 25)), 405 ); 406 print CGI::Tr( 407 CGI::th({class=>"LeftHeader"}, "SQL Admin Password:"), 408 CGI::td(CGI::password_field("add_sql_password", $add_sql_password, 25)), 409 ); 410 411 print CGI::Tr(CGI::td({colspan=>2}, 412 "The optionial SQL settings you enter below must match the settings in the DBI source" 413 . " specification " . CGI::tt($dbi_source) . ". Replace " . CGI::tt("COURSENAME") 414 . " with the course name you entered above." 415 ) 416 ); 417 print CGI::Tr( 418 CGI::th({class=>"LeftHeader"}, "SQL Server Host:"), 419 CGI::td( 420 CGI::textfield("add_sql_host", $add_sql_host, 25), 421 CGI::br(), 422 CGI::small("Leave blank to use the default host."), 423 ), 424 ); 425 print CGI::Tr( 426 CGI::th({class=>"LeftHeader"}, "SQL Server Port:"), 427 CGI::td( 428 CGI::textfield("add_sql_port", $add_sql_port, 25), 429 CGI::br(), 430 CGI::small("Leave blank to use the default port."), 431 ), 432 ); 433 434 print CGI::Tr( 435 CGI::th({class=>"LeftHeader"}, "SQL Database Name:"), 436 CGI::td( 437 CGI::textfield("add_sql_database", $add_sql_database, 25), 438 CGI::br(), 439 CGI::small("Leave blank to use the name ", CGI::tt("webwork_COURSENAME"), "."), 440 ), 441 ); 442 print CGI::Tr( 443 CGI::th({class=>"LeftHeader"}, "WeBWorK Host:"), 444 CGI::td( 445 CGI::textfield("add_sql_wwhost", $add_sql_wwhost || "localhost", 25), 446 CGI::br(), 447 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."), 448 ), 449 ); 450 print CGI::end_table(); 451 } elsif ($dbLayout eq "gdbm") { 452 print CGI::start_table({class=>"FormLayout"}); 453 print CGI::Tr( 454 CGI::th({class=>"LeftHeader"}, "GDBM Global User ID:"), 455 CGI::td(CGI::textfield("add_gdbm_globalUserID", $add_gdbm_globalUserID || "global_user", 25)), 456 ); 457 print CGI::end_table(); 458 } 459 460 print CGI::end_td(); 461 print CGI::end_Tr(); 462 print CGI::end_table(); 463 } 464 465 print CGI::p({style=>"text-align: center"}, CGI::submit("add_course", "Add Course")); 466 467 print CGI::end_form(); 468 } 469 470 sub add_course_validate { 471 my ($self) = @_; 472 my $r = $self->r; 473 my $ce = $r->ce; 474 #my $db = $r->db; 475 #my $authz = $r->authz; 476 #my $urlpath = $r->urlpath; 477 478 my $add_courseID = $r->param("add_courseID") || ""; 479 my $add_courseTitle = $r->param("add_courseTitle") || ""; 480 my $add_courseInstitution = $r->param("add_courseInstitution") || ""; 481 482 my $add_admin_users = $r->param("add_admin_users") || ""; 483 484 my $add_initial_userID = $r->param("add_initial_userID") || ""; 485 my $add_initial_password = $r->param("add_initial_password") || ""; 486 my $add_initial_confirmPassword = $r->param("add_initial_confirmPassword") || ""; 487 my $add_initial_firstName = $r->param("add_initial_firstName") || ""; 488 my $add_initial_lastName = $r->param("add_initial_lastName") || ""; 489 my $add_initial_email = $r->param("add_initial_email") || ""; 490 491 my $add_templates_course = $r->param("add_templates_course") || ""; 492 493 my $add_dbLayout = $r->param("add_dbLayout") || ""; 494 my $add_sql_host = $r->param("add_sql_host") || ""; 495 my $add_sql_port = $r->param("add_sql_port") || ""; 496 my $add_sql_username = $r->param("add_sql_username") || ""; 497 my $add_sql_password = $r->param("add_sql_password") || ""; 498 my $add_sql_database = $r->param("add_sql_database") || ""; 499 my $add_sql_wwhost = $r->param("add_sql_wwhost") || ""; 500 my $add_gdbm_globalUserID = $r->param("add_gdbm_globalUserID") || ""; 501 502 my @errors; 503 504 if ($add_courseID eq "") { 505 push @errors, "You must specify a course ID."; 506 } 507 if (grep { $add_courseID eq $_ } listCourses($ce)) { 508 push @errors, "A course with ID $add_courseID already exists."; 509 } 510 if ($add_courseTitle eq "") { 511 push @errors, "You must specify a course title."; 512 } 513 if ($add_courseInstitution eq "") { 514 push @errors, "You must specify an institution for this course."; 515 } 516 517 if ($add_initial_userID ne "") { 518 if ($add_initial_password eq "") { 519 push @errors, "You must specify a password for the initial instructor."; 520 } 521 if ($add_initial_confirmPassword eq "") { 522 push @errors, "You must confirm the password for the initial instructor."; 523 } 524 if ($add_initial_password ne $add_initial_confirmPassword) { 525 push @errors, "The password and password confirmation for the instructor must match."; 526 } 527 if ($add_initial_firstName eq "") { 528 push @errors, "You must specify a first name for the initial instructor."; 529 } 530 if ($add_initial_lastName eq "") { 531 push @errors, "You must specify a last name for the initial instructor."; 532 } 533 if ($add_initial_email eq "") { 534 push @errors, "You must specify an email address for the initial instructor."; 535 } 536 } 537 538 if ($add_dbLayout eq "") { 539 push @errors, "You must select a database layout."; 540 } else { 541 if (exists $ce->{dbLayouts}->{$add_dbLayout}) { 542 if ($add_dbLayout eq "sql") { 543 push @errors, "You must specify the SQL admin username." if $add_sql_username eq ""; 544 push @errors, "You must specify the WeBWorK host." if $add_sql_wwhost eq ""; 545 } elsif ($add_dbLayout eq "gdbm") { 546 push @errors, "You must specify the GDBM global user ID." if $add_gdbm_globalUserID eq ""; 547 } 548 } else { 549 push @errors, "The database layout $add_dbLayout doesn't exist."; 550 } 551 } 552 553 return @errors; 554 } 555 556 sub do_add_course { 557 my ($self) = @_; 558 my $r = $self->r; 559 my $ce = $r->ce; 560 my $db = $r->db; 561 #my $authz = $r->authz; 562 my $urlpath = $r->urlpath; 563 564 my $add_courseID = $r->param("add_courseID") || ""; 565 my $add_courseTitle = $r->param("add_courseTitle") || ""; 566 my $add_courseInstitution = $r->param("add_courseInstitution") || ""; 567 568 my $add_admin_users = $r->param("add_admin_users") || ""; 569 570 my $add_initial_userID = $r->param("add_initial_userID") || ""; 571 my $add_initial_password = $r->param("add_initial_password") || ""; 572 my $add_initial_confirmPassword = $r->param("add_initial_confirmPassword") || ""; 573 my $add_initial_firstName = $r->param("add_initial_firstName") || ""; 574 my $add_initial_lastName = $r->param("add_initial_lastName") || ""; 575 my $add_initial_email = $r->param("add_initial_email") || ""; 576 577 my $add_templates_course = $r->param("add_templates_course") || ""; 578 579 my $add_dbLayout = $r->param("add_dbLayout") || ""; 580 my $add_sql_host = $r->param("add_sql_host") || ""; 581 my $add_sql_port = $r->param("add_sql_port") || ""; 582 my $add_sql_username = $r->param("add_sql_username") || ""; 583 my $add_sql_password = $r->param("add_sql_password") || ""; 584 my $add_sql_database = $r->param("add_sql_database") || ""; 585 my $add_sql_wwhost = $r->param("add_sql_wwhost") || ""; 586 my $add_gdbm_globalUserID = $r->param("add_gdbm_globalUserID") || ""; 587 588 my $ce2 = WeBWorK::CourseEnvironment->new( 589 $ce->{webworkDirs}->{root}, 590 $ce->{webworkURLs}->{root}, 591 $ce->{pg}->{directories}->{root}, 592 $add_courseID, 593 ); 594 595 my %courseOptions = ( dbLayoutName => $add_dbLayout ); 596 597 if ($add_initial_email ne "") { 598 $courseOptions{allowedRecipients} = [ $add_initial_email ]; 599 $courseOptions{feedbackRecipients} = [ $add_initial_email ]; 600 } 601 602 if ($add_dbLayout eq "gdbm") { 603 $courseOptions{globalUserID} = $add_gdbm_globalUserID if $add_gdbm_globalUserID ne ""; 604 } 605 606 my %dbOptions; 607 if ($add_dbLayout eq "sql") { 608 $dbOptions{host} = $add_sql_host if $add_sql_host ne ""; 609 $dbOptions{port} = $add_sql_port if $add_sql_port ne ""; 610 $dbOptions{username} = $add_sql_username; 611 $dbOptions{password} = $add_sql_password; 612 $dbOptions{database} = $add_sql_database || "webwork_$add_courseID"; 613 $dbOptions{wwhost} = $add_sql_wwhost; 614 } 615 616 my @users; 617 618 # copy users from current (admin) course if desired 619 if ($add_admin_users ne "") { 620 foreach my $userID ($db->listUsers) { 621 my $User = $db->getUser($userID); 622 my $Password = $db->getPassword($userID); 623 my $PermissionLevel = $db->getPermissionLevel($userID); 624 push @users, [ $User, $Password, $PermissionLevel ]; 625 } 626 } 627 628 # add initial instructor if desired 629 if ($add_initial_userID ne "") { 630 my $User = $db->newUser( 631 user_id => $add_initial_userID, 632 first_name => $add_initial_firstName, 633 last_name => $add_initial_lastName, 634 student_id => $add_initial_userID, 635 email_address => $add_initial_email, 636 status => "C", 637 ); 638 my $Password = $db->newPassword( 639 user_id => $add_initial_userID, 640 password => cryptPassword($add_initial_password), 641 ); 642 my $PermissionLevel = $db->newPermissionLevel( 643 user_id => $add_initial_userID, 644 permission => "10", 645 ); 646 push @users, [ $User, $Password, $PermissionLevel ]; 647 } 648 649 push @{$courseOptions{PRINT_FILE_NAMES_FOR}}, map { $_->[0]->user_id } @users; 650 651 my %optional_arguments; 652 if ($add_templates_course ne "") { 653 $optional_arguments{templatesFrom} = $add_templates_course; 654 } 655 656 eval { 657 addCourse( 658 courseID => $add_courseID, 659 ce => $ce2, 660 courseOptions => \%courseOptions, 661 dbOptions => \%dbOptions, 662 users => \@users, 663 %optional_arguments, 664 ); 665 }; 666 if ($@) { 667 my $error = $@; 668 print CGI::div({class=>"ResultsWithError"}, 669 CGI::p("An error occured while creating the course $add_courseID:"), 670 CGI::tt(CGI::escapeHTML($error)), 671 ); 672 # get rid of any partially built courses 673 # FIXME -- this is too fragile 674 unless ($error =~ /course exists/) { 675 eval { 676 deleteCourse( 677 courseID => $add_courseID, 678 ce => $ce2, 679 dbOptions => \%dbOptions, 680 ); 681 } 682 } 683 } else { 684 #log the action 685 writeLog($ce, "hosted_courses", join("\t", 686 "\tAdded", 687 $add_courseInstitution, 688 $add_courseTitle, 689 $add_courseID, 690 $add_initial_firstName, 691 $add_initial_lastName, 692 $add_initial_email, 693 )); 694 # add contact to admin course as student? 695 # FIXME -- should we do this? 696 print CGI::div({class=>"ResultsWithoutError"}, 697 CGI::p("Successfully created the course $add_courseID"), 698 ); 699 my $newCoursePath = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", 700 courseID => $add_courseID); 701 my $newCourseURL = $self->systemLink($newCoursePath, authen => 0); 702 print CGI::div({style=>"text-align: center"}, 703 CGI::a({href=>$newCourseURL}, "Log into $add_courseID"), 704 ); 705 } 706 707 708 } 709 710 ################################################################################ 711 712 sub delete_course_form { 713 my ($self) = @_; 714 my $r = $self->r; 715 my $ce = $r->ce; 716 #my $db = $r->db; 717 #my $authz = $r->authz; 718 #my $urlpath = $r->urlpath; 719 720 my $delete_courseID = $r->param("delete_courseID") || ""; 721 my $delete_sql_host = $r->param("delete_sql_host") || ""; 722 my $delete_sql_port = $r->param("delete_sql_port") || ""; 723 my $delete_sql_username = $r->param("delete_sql_username") || ""; 724 my $delete_sql_password = $r->param("delete_sql_password") || ""; 725 my $delete_sql_database = $r->param("delete_sql_database") || ""; 726 727 my @courseIDs = listCourses($ce); 728 @courseIDs = sort @courseIDs; 729 730 my %courseLabels; # records... heh. 731 foreach my $courseID (@courseIDs) { 732 my $tempCE = WeBWorK::CourseEnvironment->new( 733 $ce->{webworkDirs}->{root}, 734 $ce->{webworkURLs}->{root}, 735 $ce->{pg}->{directories}->{root}, 736 $courseID, 737 ); 738 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")"; 739 } 740 741 print CGI::h2("Delete Course"); 742 743 print CGI::start_form("POST", $r->uri); 744 print $self->hidden_authen_fields; 745 print $self->hidden_fields("subDisplay"); 746 747 print CGI::p("Select a course to delete."); 748 749 print CGI::table({class=>"FormLayout"}, 750 CGI::Tr( 751 CGI::th({class=>"LeftHeader"}, "Course Name:"), 752 CGI::td( 753 CGI::scrolling_list( 754 -name => "delete_courseID", 755 -values => \@courseIDs, 756 -default => $delete_courseID, 757 -size => 10, 758 -multiple => 0, 759 -labels => \%courseLabels, 760 ), 761 ), 762 ), 763 ); 764 765 print CGI::p( 766 "If the course's database layout (indicated in parentheses above) is " 767 . CGI::b("sql") . ", supply the SQL connections information requested below." 768 ); 769 770 print CGI::start_table({class=>"FormLayout"}); 771 print CGI::Tr(CGI::td({colspan=>2}, 772 "Enter the user ID and password for an SQL account with sufficient permissions to delete an existing database." 773 ) 774 ); 775 print CGI::Tr( 776 CGI::th({class=>"LeftHeader"}, "SQL Admin Username:"), 777 CGI::td(CGI::textfield("delete_sql_username", $delete_sql_username, 25)), 778 ); 779 print CGI::Tr( 780 CGI::th({class=>"LeftHeader"}, "SQL Admin Password:"), 781 CGI::td(CGI::password_field("delete_sql_password", $delete_sql_password, 25)), 782 ); 783 784 #print CGI::Tr(CGI::td({colspan=>2}, 785 # "The optionial SQL settings you enter below must match the settings in the DBI source" 786 # . " specification " . CGI::tt($dbi_source) . ". Replace " . CGI::tt("COURSENAME") 787 # . " with the course name you entered above." 788 # ) 789 #); 790 print CGI::Tr( 791 CGI::th({class=>"LeftHeader"}, "SQL Server Host:"), 792 CGI::td( 793 CGI::textfield("delete_sql_host", $delete_sql_host, 25), 794 CGI::br(), 795 CGI::small("Leave blank to use the default host."), 796 ), 797 ); 798 print CGI::Tr( 799 CGI::th({class=>"LeftHeader"}, "SQL Server Port:"), 800 CGI::td( 801 CGI::textfield("delete_sql_port", $delete_sql_port, 25), 802 CGI::br(), 803 CGI::small("Leave blank to use the default port."), 804 ), 805 ); 806 807 print CGI::Tr( 808 CGI::th({class=>"LeftHeader"}, "SQL Database Name:"), 809 CGI::td( 810 CGI::textfield("delete_sql_database", $delete_sql_database, 25), 811 CGI::br(), 812 CGI::small("Leave blank to use the name ", CGI::tt("webwork_COURSENAME"), "."), 813 ), 814 ); 815 print CGI::end_table(); 816 817 print CGI::p({style=>"text-align: center"}, CGI::submit("delete_course", "Delete Course")); 818 819 print CGI::end_form(); 820 } 821 822 sub delete_course_validate { 823 my ($self) = @_; 824 my $r = $self->r; 825 my $ce = $r->ce; 826 #my $db = $r->db; 827 #my $authz = $r->authz; 828 my $urlpath = $r->urlpath; 829 830 my $delete_courseID = $r->param("delete_courseID") || ""; 831 my $delete_sql_host = $r->param("delete_sql_host") || ""; 832 my $delete_sql_port = $r->param("delete_sql_port") || ""; 833 my $delete_sql_username = $r->param("delete_sql_username") || ""; 834 my $delete_sql_password = $r->param("delete_sql_password") || ""; 835 my $delete_sql_database = $r->param("delete_sql_database") || ""; 836 837 my @errors; 838 839 if ($delete_courseID eq "") { 840 push @errors, "You must specify a course name."; 841 } elsif ($delete_courseID eq $urlpath->arg("courseID")) { 842 push @errors, "You cannot delete the course you are currently using."; 843 } 844 845 my $ce2 = WeBWorK::CourseEnvironment->new( 846 $ce->{webworkDirs}->{root}, 847 $ce->{webworkURLs}->{root}, 848 $ce->{pg}->{directories}->{root}, 849 $delete_courseID, 850 ); 851 852 if ($ce2->{dbLayoutName} eq "sql") { 853 push @errors, "You must specify the SQL admin username." if $delete_sql_username eq ""; 854 #push @errors, "You must specify the SQL admin password." if $delete_sql_password eq ""; 855 #push @errors, "You must specify the SQL database name." if $delete_sql_database eq ""; 856 } 857 858 return @errors; 859 } 860 861 sub delete_course_confirm { 862 my ($self) = @_; 863 my $r = $self->r; 864 my $ce = $r->ce; 865 #my $db = $r->db; 866 #my $authz = $r->authz; 867 #my $urlpath = $r->urlpath; 868 869 print CGI::h2("Delete Course"); 870 871 my $delete_courseID = $r->param("delete_courseID") || ""; 872 my $delete_sql_host = $r->param("delete_sql_host") || ""; 873 my $delete_sql_port = $r->param("delete_sql_port") || ""; 874 my $delete_sql_database = $r->param("delete_sql_database") || ""; 875 876 my $ce2 = WeBWorK::CourseEnvironment->new( 877 $ce->{webworkDirs}->{root}, 878 $ce->{webworkURLs}->{root}, 879 $ce->{pg}->{directories}->{root}, 880 $delete_courseID, 881 ); 882 883 if ($ce2->{dbLayoutName} eq "sql") { 884 print CGI::p("Are you sure you want to delete the course " . CGI::b($delete_courseID) 885 . "? All course files and data and the following database will be destroyed." 886 . " There is no undo available."); 887 888 print CGI::table({class=>"FormLayout"}, 889 CGI::Tr( 890 CGI::th({class=>"LeftHeader"}, "SQL Server Host:"), 891 CGI::td($delete_sql_host || "system default"), 892 ), 893 CGI::Tr( 894 CGI::th({class=>"LeftHeader"}, "SQL Server Port:"), 895 CGI::td($delete_sql_port || "system default"), 896 ), 897 CGI::Tr( 898 CGI::th({class=>"LeftHeader"}, "SQL Database Name:"), 899 CGI::td($delete_sql_database || "webwork_$delete_courseID"), 900 ), 901 ); 902 } else { 903 print CGI::p("Are you sure you want to delete the course " . CGI::b($delete_courseID) 904 . "? All course files and data will be destroyed. There is no undo available."); 905 } 906 907 print CGI::start_form("POST", $r->uri); 908 print $self->hidden_authen_fields; 909 print $self->hidden_fields("subDisplay"); 910 print $self->hidden_fields(qw/delete_courseID delete_sql_host delete_sql_port delete_sql_username delete_sql_password delete_sql_database/); 911 912 print CGI::p({style=>"text-align: center"}, 913 CGI::submit("decline_delete_course", "Don't delete"), 914 " ", 915 CGI::submit("confirm_delete_course", "Delete"), 916 ); 917 918 print CGI::end_form(); 919 } 920 921 sub do_delete_course { 922 my ($self) = @_; 923 my $r = $self->r; 924 my $ce = $r->ce; 925 #my $db = $r->db; 926 #my $authz = $r->authz; 927 #my $urlpath = $r->urlpath; 928 929 my $delete_courseID = $r->param("delete_courseID") || ""; 930 my $delete_sql_host = $r->param("delete_sql_host") || ""; 931 my $delete_sql_port = $r->param("delete_sql_port") || ""; 932 my $delete_sql_username = $r->param("delete_sql_username") || ""; 933 my $delete_sql_password = $r->param("delete_sql_password") || ""; 934 my $delete_sql_database = $r->param("delete_sql_database") || ""; 935 936 my $ce2 = WeBWorK::CourseEnvironment->new( 937 $ce->{webworkDirs}->{root}, 938 $ce->{webworkURLs}->{root}, 939 $ce->{pg}->{directories}->{root}, 940 $delete_courseID, 941 ); 942 943 my %dbOptions; 944 if ($ce2->{dbLayoutName} eq "sql") { 945 $dbOptions{host} = $delete_sql_host if $delete_sql_host ne ""; 946 $dbOptions{port} = $delete_sql_port if $delete_sql_port ne ""; 947 $dbOptions{username} = $delete_sql_username; 948 $dbOptions{password} = $delete_sql_password; 949 $dbOptions{database} = $delete_sql_database || "webwork_$delete_courseID"; 950 } 951 952 eval { 953 deleteCourse( 954 courseID => $delete_courseID, 955 ce => $ce2, 956 dbOptions => \%dbOptions, 957 ); 958 }; 959 960 if ($@) { 961 my $error = $@; 962 print CGI::div({class=>"ResultsWithError"}, 963 CGI::p("An error occured while deleting the course $delete_courseID:"), 964 CGI::tt(CGI::escapeHTML($error)), 965 ); 966 } else { 967 print CGI::div({class=>"ResultsWithoutError"}, 968 CGI::p("Successfully deleted the course $delete_courseID."), 969 ); 970 writeLog($ce, "hosted_courses", join("\t", 971 "\tDeleted", 972 "", 973 "", 974 $delete_courseID, 975 )); 976 print CGI::start_form("POST", $r->uri); 977 print $self->hidden_authen_fields; 978 print $self->hidden_fields("subDisplay"); 979 980 print CGI::p({style=>"text-align: center"}, CGI::submit("decline_delete_course", "OK"),); 981 982 print CGI::end_form(); 983 } 984 } 985 986 ################################################################################ 987 988 sub export_database_form { 989 my ($self) = @_; 990 my $r = $self->r; 991 my $ce = $r->ce; 992 #my $db = $r->db; 993 #my $authz = $r->authz; 994 #my $urlpath = $r->urlpath; 995 996 my @tables = keys %{$ce->{dbLayout}}; 997 998 my $export_courseID = $r->param("export_courseID") || ""; 999 my @export_tables = $r->param("export_tables"); 1000 1001 @export_tables = @tables unless @export_tables; 1002 1003 my @courseIDs = listCourses($ce); 1004 @courseIDs = sort @courseIDs; 1005 1006 my %courseLabels; # records... heh. 1007 foreach my $courseID (@courseIDs) { 1008 my $tempCE = WeBWorK::CourseEnvironment->new( 1009 $ce->{webworkDirs}->{root}, 1010 $ce->{webworkURLs}->{root}, 1011 $ce->{pg}->{directories}->{root}, 1012 $courseID, 1013 ); 1014 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")"; 1015 } 1016 1017 print CGI::h2("Export Database"); 1018 1019 print CGI::start_form("GET", $r->uri); 1020 print $self->hidden_authen_fields; 1021 print $self->hidden_fields("subDisplay"); 1022 1023 print CGI::p("Select a course to export the course's database."); 1024 1025 print CGI::table({class=>"FormLayout"}, 1026 CGI::Tr( 1027 CGI::th({class=>"LeftHeader"}, "Course Name:"), 1028 CGI::td( 1029 CGI::scrolling_list( 1030 -name => "export_courseID", 1031 -values => \@courseIDs, 1032 -default => $export_courseID, 1033 -size => 10, 1034 -multiple => 0, 1035 -labels => \%courseLabels, 1036 ), 1037 ), 1038 ), 1039 CGI::Tr( 1040 CGI::th({class=>"LeftHeader"}, "Tables to Export:"), 1041 CGI::td( 1042 CGI::checkbox_group( 1043 -name => "export_tables", 1044 -values => \@tables, 1045 -default => \@export_tables, 1046 -linebreak => 1, 1047 ), 1048 ), 1049 ), 1050 ); 1051 1052 print CGI::p({style=>"text-align: center"}, CGI::submit("export_database", "Export Database")); 1053 1054 print CGI::end_form(); 1055 } 1056 1057 sub export_database_validate { 1058 my ($self) = @_; 1059 my $r = $self->r; 1060 #my $ce = $r->ce; 1061 #my $db = $r->db; 1062 #my $authz = $r->authz; 1063 #my $urlpath = $r->urlpath; 1064 1065 my $export_courseID = $r->param("export_courseID") || ""; 1066 my @export_tables = $r->param("export_tables"); 1067 1068 my @errors; 1069 1070 if ($export_courseID eq "") { 1071 push @errors, "You must specify a course name."; 1072 } 1073 1074 unless (@export_tables) { 1075 push @errors, "You must specify at least one table to export."; 1076 } 1077 1078 return @errors; 1079 } 1080 1081 sub do_export_database { 1082 my ($self) = @_; 1083 my $r = $self->r; 1084 my $ce = $r->ce; 1085 #my $db = $r->db; 1086 #my $authz = $r->authz; 1087 my $urlpath = $r->urlpath; 1088 1089 my $export_courseID = $r->param("export_courseID"); 1090 my @export_tables = $r->param("export_tables"); 1091 1092 my $ce2 = WeBWorK::CourseEnvironment->new( 1093 $ce->{webworkDirs}->{root}, 1094 $ce->{webworkURLs}->{root}, 1095 $ce->{pg}->{directories}->{root}, 1096 $export_courseID, 1097 ); 1098 1099 my $db2 = new WeBWorK::DB($ce2->{dbLayout}); 1100 1101 #my ($fh, $export_file) = tempfile("db_export_XXXXXX", DIR => $ce->{webworkDirs}->{tmp}); 1102 #my ($random_chars) = $export_file =~ m/db_export_(\w+)$/; 1103 1104 my @errors; 1105 1106 eval { 1107 @errors = dbExport( 1108 db => $db2, 1109 #xml => $fh, 1110 xml => *STDOUT, 1111 tables => \@export_tables, 1112 ); 1113 }; 1114 1115 #push @errors, "Fatal exception: $@" if $@; 1116 # 1117 #if (@errors) { 1118 # print CGI::div({class=>"ResultsWithError"}, 1119 # CGI::p("An error occured while exporting the database of course $export_courseID:"), 1120 # CGI::ul(CGI::li(\@errors)), 1121 # ); 1122 #} else { 1123 # print CGI::div({class=>"ResultsWithoutError"}, 1124 # CGI::p("Export succeeded."), 1125 # ); 1126 # 1127 # print CGI::div({style=>"text-align: center"}, 1128 # CGI::a({href=>$self->systemLink($urlpath, params=>{download_exported_database=>$random_chars, export_courseID=>undef})}, "Download Exported Database"), 1129 # ); 1130 #} 1131 } 1132 1133 ################################################################################ 1134 1135 sub import_database_form { 1136 my ($self) = @_; 1137 my $r = $self->r; 1138 my $ce = $r->ce; 1139 #my $db = $r->db; 1140 #my $authz = $r->authz; 1141 #my $urlpath = $r->urlpath; 1142 1143 my @tables = keys %{$ce->{dbLayout}}; 1144 1145 my $import_file = $r->param("import_file") || ""; 1146 my $import_courseID = $r->param("import_courseID") || ""; 1147 my @import_tables = $r->param("import_tables"); 1148 my $import_conflict = $r->param("import_conflict") || "skip"; 1149 1150 @import_tables = @tables unless @import_tables; 1151 1152 my @courseIDs = listCourses($ce); 1153 @courseIDs = sort @courseIDs; 1154 1155 1156 my %courseLabels; # records... heh. 1157 foreach my $courseID (@courseIDs) { 1158 my $tempCE = WeBWorK::CourseEnvironment->new( 1159 $ce->{webworkDirs}->{root}, 1160 $ce->{webworkURLs}->{root}, 1161 $ce->{pg}->{directories}->{root}, 1162 $courseID, 1163 ); 1164 $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")"; 1165 } 1166 1167 print CGI::h2("Import Database"); 1168 1169 print CGI::start_form("POST", $r->uri, &CGI::MULTIPART); 1170 print $self->hidden_authen_fields; 1171 print $self->hidden_fields("subDisplay"); 1172 1173 print CGI::table({class=>"FormLayout"}, 1174 CGI::Tr( 1175 CGI::th({class=>"LeftHeader"}, "Database XML File:"), 1176 CGI::td( 1177 CGI::filefield( 1178 -name => "import_file", 1179 -size => 50, 1180 ), 1181 ), 1182 ), 1183 CGI::Tr( 1184 CGI::th({class=>"LeftHeader"}, "Tables to Import:"), 1185 CGI::td( 1186 CGI::checkbox_group( 1187 -name => "import_tables", 1188 -values => \@tables, 1189 -default => \@import_tables, 1190 -linebreak => 1, 1191 ), 1192 ), 1193 ), 1194 CGI::Tr( 1195 CGI::th({class=>"LeftHeader"}, "Import into Course:"), 1196 CGI::td( 1197 CGI::scrolling_list( 1198 -name => "import_courseID", 1199 -values => \@courseIDs, 1200 -default => $import_courseID, 1201 -size => 10, 1202 -multiple => 0, 1203 -labels => \%courseLabels, 1204 ), 1205 ), 1206 ), 1207 CGI::Tr( 1208 CGI::th({class=>"LeftHeader"}, "Conflicts:"), 1209 CGI::td( 1210 CGI::radio_group( 1211 -name => "import_conflict", 1212 -values => [qw/skip replace/], 1213 -default => $import_conflict, 1214 -linebreak=>'true', 1215 -labels => { 1216 skip => "Skip duplicate records", 1217 replace => "Replace duplicate records", 1218 }, 1219 ), 1220 ), 1221 ), 1222 ); 1223 1224 print CGI::p({style=>"text-align: center"}, CGI::submit("import_database", "Import Database")); 1225 1226 print CGI::end_form(); 1227 } 1228 1229 sub import_database_validate { 1230 my ($self) = @_; 1231 my $r = $self->r; 1232 #my $ce = $r->ce; 1233 #my $db = $r->db; 1234 #my $authz = $r->authz; 1235 #my $urlpath = $r->urlpath; 1236 1237 my $import_file = $r->param("import_file") || ""; 1238 my $import_courseID = $r->param("import_courseID") || ""; 1239 my @import_tables = $r->param("import_tables"); 1240 #my $import_conflict = $r->param("import_conflict") || "skip"; # not checked 1241 1242 my @errors; 1243 1244 if ($import_file eq "") { 1245 push @errors, "You must specify a database file to upload."; 1246 } 1247 1248 if ($import_courseID eq "") { 1249 push @errors, "You must specify a course name."; 1250 } 1251 1252 unless (@import_tables) { 1253 push @errors, "You must specify at least one table to import."; 1254 } 1255 1256 return @errors; 1257 } 1258 1259 sub do_import_database { 1260 my ($self) = @_; 1261 my $r = $self->r; 1262 my $ce = $r->ce; 1263 #my $db = $r->db; 1264 #my $authz = $r->authz; 1265 my $urlpath = $r->urlpath; 1266 1267 my $import_file = $r->param("import_file"); 1268 my $import_courseID = $r->param("import_courseID"); 1269 my @import_tables = $r->param("import_tables"); 1270 my $import_conflict = $r->param("import_conflict") || "skip"; # need default -- not checked above 1271 1272 my $ce2 = WeBWorK::CourseEnvironment->new( 1273 $ce->{webworkDirs}->{root}, 1274 $ce->{webworkURLs}->{root}, 1275 $ce->{pg}->{directories}->{root}, 1276 $import_courseID, 1277 ); 1278 1279 my $db2 = new WeBWorK::DB($ce2->{dbLayout}); 1280 1281 # retrieve upload from upload cache 1282 my ($id, $hash) = split /\s+/, $import_file; 1283 my $upload = WeBWorK::Upload->retrieve($id, $hash, 1284 dir => $ce->{webworkDirs}->{uploadCache} 1285 ); 1286 1287 my @errors; 1288 1289 eval { 1290 @errors = dbImport( 1291 db => $db2, 1292 xml => $upload->fileHandle, 1293 tables => \@import_tables, 1294 conflict => $import_conflict, 1295 ); 1296 }; 1297 1298 $upload->dispose; 1299 1300 push @errors, "Fatal exception: $@" if $@; 1301 1302 if (@errors) { 1303 print CGI::div({class=>"ResultsWithError"}, 1304 CGI::p("An error occured while importing the database of course $import_courseID:"), 1305 CGI::ul(CGI::li(\@errors)), 1306 ); 1307 } else { 1308 print CGI::div({class=>"ResultsWithoutError"}, 1309 CGI::p("Import succeeded."), 1310 ); 1311 } 1312 } 1313 1314 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |