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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4527 - (download) (as text) (annotate)
Tue Sep 26 15:57:41 2006 UTC (6 years, 7 months ago) by sh002i
Original Path: trunk/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm
File size: 57830 byte(s)
remove several useless "use Data::Dumper" lines

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v 1.58 2006/09/25 22:14:52 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 qw(-nosticky );
   29 use WeBWorK::CGI;
   30 use File::Temp qw/tempfile/;
   31 use WeBWorK::CourseEnvironment;
   32 use IO::File;
   33 use WeBWorK::Debug;
   34 use WeBWorK::Utils qw(cryptPassword writeLog listFilesRecursive);
   35 use WeBWorK::Utils::CourseManagement qw(addCourse renameCourse deleteCourse listCourses archiveCourse
   36                                         listArchivedCourses unarchiveCourse);
   37 use WeBWorK::Utils::DBImportExport qw(dbExport dbImport);
   38 
   39 use constant IMPORT_EXPORT_WARNING => "The ability to import and export
   40 databases is still under development. It seems to work but it is <b>VERY</b>
   41 slow on large courses.  You may prefer to use webwork2/bin/wwdb  or the mysql
   42 dump facility for archiving large courses. Please send bug reports if you find
   43 errors.";
   44 
   45 sub pre_header_initialize {
   46   my ($self) = @_;
   47   my $r = $self->r;
   48   my $ce = $r->ce;
   49   my $db = $r->db;
   50   my $authz = $r->authz;
   51   my $urlpath = $r->urlpath;
   52   my $user        = $r->param('user');
   53 
   54   # check permissions
   55   unless ($authz->hasPermissions($user, "create_and_delete_courses")) {
   56     $self->addmessage( CGI::div({class=>'ResultsWithError'},"$user is not authorized to create or delete courses") );
   57     return;
   58   }
   59 
   60   # get result and send to message
   61   my $status_message = $r->param("status_message");
   62   $self->addmessage(CGI::p("$status_message")) if $status_message;
   63 
   64   ## if the user is asking for the downloaded database...
   65   #if (defined $r->param("download_exported_database")) {
   66   # my $courseID = $r->param("export_courseID");
   67   # my $random_chars = $r->param("download_exported_database");
   68   #
   69   # die "courseID not specified" unless defined $courseID;
   70   # die "invalid file specification" unless $random_chars =~ m/^\w+$/;
   71   #
   72   # my $tempdir = $ce->{webworkDirs}->{tmp};
   73   # my $export_file = "$tempdir/db_export_$random_chars";
   74   #
   75   # $self->reply_with_file("application/xml", $export_file, "${courseID}_database.xml", 0);
   76   #
   77   # return "";
   78   #}
   79   #
   80   ## otherwise...
   81 
   82   my @errors;
   83   my $method_to_call;
   84 
   85   my $subDisplay = $r->param("subDisplay");
   86   if (defined $subDisplay) {
   87 
   88     if ($subDisplay eq "add_course") {
   89       if (defined $r->param("add_course")) {
   90         @errors = $self->add_course_validate;
   91         if (@errors) {
   92           $method_to_call = "add_course_form";
   93         } else {
   94           $method_to_call = "do_add_course";
   95         }
   96       } else {
   97         $method_to_call = "add_course_form";
   98       }
   99     }
  100 
  101     elsif ($subDisplay eq "rename_course") {
  102       if (defined $r->param("rename_course")) {
  103         @errors = $self->rename_course_validate;
  104         if (@errors) {
  105           $method_to_call = "rename_course_form";
  106         } else {
  107           $method_to_call = "do_rename_course";
  108         }
  109       } else {
  110         $method_to_call = "rename_course_form";
  111       }
  112     }
  113 
  114     elsif ($subDisplay eq "delete_course") {
  115       if (defined $r->param("delete_course")) {
  116         # validate or confirm
  117         @errors = $self->delete_course_validate;
  118         if (@errors) {
  119           $method_to_call = "delete_course_form";
  120         } else {
  121           $method_to_call = "delete_course_confirm";
  122         }
  123       } elsif (defined $r->param("confirm_delete_course")) {
  124         # validate and delete
  125         @errors = $self->delete_course_validate;
  126         if (@errors) {
  127           $method_to_call = "delete_course_form";
  128         } else {
  129           $method_to_call = "do_delete_course";
  130         }
  131       } else {
  132         # form only
  133         $method_to_call = "delete_course_form";
  134       }
  135     }
  136 
  137     elsif ($subDisplay eq "export_database") {
  138       if (defined $r->param("export_database")) {
  139         @errors = $self->export_database_validate;
  140         if (@errors) {
  141           $method_to_call = "export_database_form";
  142         } else {
  143           # we have to do something special here, since we're sending
  144           # the database as we export it. $method_to_call still gets
  145           # set here, but it gets caught by header() and content()
  146           # below instead of by body().
  147           $method_to_call = "do_export_database";
  148         }
  149       } else {
  150         $method_to_call = "export_database_form";
  151       }
  152     }
  153 
  154     elsif ($subDisplay eq "import_database") {
  155       if (defined $r->param("import_database")) {
  156         @errors = $self->import_database_validate;
  157         if (@errors) {
  158           $method_to_call = "import_database_form";
  159         } else {
  160           $method_to_call = "do_import_database";
  161         }
  162       } else {
  163         $method_to_call = "import_database_form";
  164       }
  165     }
  166 
  167     elsif ($subDisplay eq "archive_course") {
  168       if (defined $r->param("archive_course")) {
  169         # validate or confirm
  170         @errors = $self->archive_course_validate;
  171         if (@errors) {
  172           $method_to_call = "archive_course_form";
  173         } else {
  174           $method_to_call = "archive_course_confirm";
  175         }
  176       } elsif (defined $r->param("confirm_archive_course")) {
  177         # validate and archive
  178         @errors = $self->archive_course_validate;
  179         if (@errors) {
  180           $method_to_call = "archive_course_form";
  181         } else {
  182           $method_to_call = "do_archive_course";
  183         }
  184       } else {
  185         # form only
  186         $method_to_call = "archive_course_form";
  187       }
  188     }
  189     elsif ($subDisplay eq "unarchive_course") {
  190       if (defined $r->param("unarchive_course")) {
  191         # validate or confirm
  192         @errors = $self->unarchive_course_validate;
  193         if (@errors) {
  194           $method_to_call = "unarchive_course_form";
  195         } else {
  196           $method_to_call = "unarchive_course_confirm";
  197         }
  198       } elsif (defined $r->param("confirm_unarchive_course")) {
  199         # validate and archive
  200         @errors = $self->unarchive_course_validate;
  201         if (@errors) {
  202           $method_to_call = "unarchive_course_form";
  203         } else {
  204           $method_to_call = "do_unarchive_course";
  205         }
  206       } else {
  207         # form only
  208         $method_to_call = "unarchive_course_form";
  209       }
  210     }
  211     else {
  212       @errors = "Unrecognized sub-display @{[ CGI::b($subDisplay) ]}.";
  213     }
  214 
  215   }
  216 
  217   $self->{errors} = \@errors;
  218   $self->{method_to_call} = $method_to_call;
  219 }
  220 
  221 sub header {
  222   my ($self) = @_;
  223   my $method_to_call = $self->{method_to_call};
  224 #   if (defined $method_to_call and $method_to_call eq "do_export_database") {
  225 #     my $r = $self->r;
  226 #     my $courseID = $r->param("export_courseID");
  227 #     $r->content_type("application/octet-stream");
  228 #     $r->header_out("Content-Disposition" => "attachment; filename=\"${courseID}_database.xml\"");
  229 #     $r->send_http_header;
  230 #   } else {
  231     $self->SUPER::header;
  232 # }
  233 }
  234 
  235 # sends:
  236 #
  237 # HTTP/1.1 200 OK
  238 # Date: Fri, 09 Jul 2004 19:05:55 GMT
  239 # Server: Apache/1.3.27 (Unix) mod_perl/1.27
  240 # Content-Disposition: attachment; filename="mth143_database.xml"
  241 # Connection: close
  242 # Content-Type: application/octet-stream
  243 
  244 sub content {
  245   my ($self) = @_;
  246   my $method_to_call = $self->{method_to_call};
  247   if (defined $method_to_call and $method_to_call eq "do_export_database") {
  248     #$self->do_export_database;
  249     $self->SUPER::content;
  250   } else {
  251     $self->SUPER::content;
  252   }
  253 }
  254 
  255 sub body {
  256   my ($self) = @_;
  257   my $r = $self->r;
  258   my $ce = $r->ce;
  259   my $db = $r->db;
  260   my $authz = $r->authz;
  261   my $urlpath = $r->urlpath;
  262 
  263   my $user = $r->param('user');
  264 
  265   # check permissions
  266   unless ($authz->hasPermissions($user, "create_and_delete_courses")) {
  267     return "";
  268   }
  269   my $method_to_call = $self->{method_to_call};
  270   my $methodMessage ="";
  271 
  272   (defined($method_to_call) and $method_to_call eq "do_export_database") && do {
  273       my @export_courseID = $r->param("export_courseID");
  274       my $course_ids = join(", ", @export_courseID);
  275     $methodMessage  = CGI::p("Exporting database for course(s) $course_ids").
  276     CGI::p(".... please wait....
  277     If your browser times out you will
  278     still be able to download the exported database using the
  279     file manager.").CGI::hr();
  280   };
  281 
  282 
  283   print CGI::p({style=>"text-align: center"},
  284     CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"add_course",add_admin_users=>1,
  285                add_dbLayout=>'sql_single',
  286                add_templates_course => $ce->{siteDefaults}->{default_templates_course} ||""}
  287                )},
  288                "Add Course"
  289     ),
  290     " | ",
  291     CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"rename_course"})}, "Rename Course"),
  292     " | ",
  293     CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"delete_course"})}, "Delete Course"),
  294     " | ",
  295     CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"export_database"})}, "Export Database"),
  296     " | ",
  297     CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"import_database"})}, "Import Database"),
  298     " | ",
  299     CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"archive_course"})}, "Archive Course"),
  300      "|",
  301     CGI::a({href=>$self->systemLink($urlpath, params=>{subDisplay=>"unarchive_course"})}, "Unarchive Course"),
  302     CGI::hr(),
  303     $methodMessage,
  304 
  305   );
  306 
  307   my @errors = @{$self->{errors}};
  308 
  309 
  310   if (@errors) {
  311     print CGI::div({class=>"ResultsWithError"},
  312       CGI::p("Please correct the following errors and try again:"),
  313       CGI::ul(CGI::li(\@errors)),
  314     );
  315   }
  316 
  317   if (defined $method_to_call and $method_to_call ne "") {
  318     $self->$method_to_call;
  319   } else {
  320 
  321     print CGI::h2("Courses");
  322 
  323     print CGI::start_ol();
  324 
  325     my @courseIDs = listCourses($ce);
  326     foreach my $courseID (sort {lc($a) cmp lc($b) } @courseIDs) {
  327       next if $courseID eq "admin"; # done already above
  328       my $urlpath = $r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", courseID => $courseID);
  329       my $tempCE = WeBWorK::CourseEnvironment->new(
  330         $ce->{webworkDirs}->{root},
  331         $ce->{webworkURLs}->{root},
  332         $ce->{pg}->{directories}->{root},
  333         $courseID,
  334       );
  335       print CGI::li(CGI::a({href=>$self->systemLink($urlpath, authen => 0)}, $courseID),
  336         CGI::code(
  337           $tempCE->{dbLayoutName},
  338         ),
  339         (-r $tempCE->{courseFiles}->{environment}) ? "" : CGI::i(", missing course.conf"),
  340 
  341       );
  342 
  343     }
  344 
  345     print CGI::end_ol();
  346 
  347     print CGI::h2("Archived Courses");
  348     print CGI::start_ol();
  349 
  350     @courseIDs = listArchivedCourses($ce);
  351     foreach my $courseID (sort {lc($a) cmp lc($b) } @courseIDs) {
  352       print CGI::li($courseID),
  353     }
  354 
  355     print CGI::end_ol();
  356   }
  357   return "";
  358 }
  359 
  360 ################################################################################
  361 
  362 sub add_course_form {
  363   my ($self) = @_;
  364   my $r = $self->r;
  365   my $ce = $r->ce;
  366   #my $db = $r->db;
  367   #my $authz = $r->authz;
  368   #my $urlpath = $r->urlpath;
  369 
  370   my $add_courseID                     = $r->param("add_courseID") || "";
  371   my $add_courseTitle                  = $r->param("add_courseTitle") || "";
  372   my $add_courseInstitution            = $r->param("add_courseInstitution") || "";
  373 
  374   my $add_admin_users                  = $r->param("add_admin_users") || "";
  375 
  376   my $add_initial_userID               = $r->param("add_initial_userID") || "";
  377   my $add_initial_password             = $r->param("add_initial_password") || "";
  378   my $add_initial_confirmPassword      = $r->param("add_initial_confirmPassword") || "";
  379   my $add_initial_firstName            = $r->param("add_initial_firstName") || "";
  380   my $add_initial_lastName             = $r->param("add_initial_lastName") || "";
  381   my $add_initial_email                = $r->param("add_initial_email") || "";
  382 
  383   my $add_templates_course             = $r->param("add_templates_course") || "";
  384 
  385   my $add_dbLayout                     = $r->param("add_dbLayout") || "";
  386 
  387   my @dbLayouts = do {
  388     my @ordered_layouts;
  389     foreach my $layout (@{$ce->{dbLayout_order}}) {
  390       if (exists $ce->{dbLayouts}->{$layout}) {
  391         push @ordered_layouts, $layout;
  392       }
  393     }
  394 
  395     my %ordered_layouts; @ordered_layouts{@ordered_layouts} = ();
  396     my @other_layouts;
  397     foreach my $layout (keys %{ $ce->{dbLayouts} }) {
  398       unless (exists $ordered_layouts{$layout}) {
  399         push @other_layouts, $layout;
  400       }
  401     }
  402 
  403     (@ordered_layouts, @other_layouts);
  404   };
  405 
  406   my $ce2 = WeBWorK::CourseEnvironment->new(
  407     $ce->{webworkDirs}->{root},
  408     $ce->{webworkURLs}->{root},
  409     $ce->{pg}->{directories}->{root},
  410     "COURSENAME",
  411   );
  412 
  413   my @existingCourses = listCourses($ce);
  414   @existingCourses = sort { lc($a) cmp lc ($b) } @existingCourses; #make sort case insensitive
  415 
  416   print CGI::h2("Add Course");
  417 
  418   print CGI::start_form(-method=>"POST", -action=>$r->uri);
  419   print $self->hidden_authen_fields;
  420   print $self->hidden_fields("subDisplay");
  421 
  422   print CGI::p("Specify an ID, title, and institution for the new course. The course ID may contain only letters, numbers, hyphens, and underscores.");
  423 
  424   print CGI::table({class=>"FormLayout"},
  425     CGI::Tr({},
  426       CGI::th({class=>"LeftHeader"}, "Course ID:"),
  427       CGI::td(CGI::textfield(-name=>"add_courseID", -value=>$add_courseID, -size=>25)),
  428     ),
  429     CGI::Tr({},
  430       CGI::th({class=>"LeftHeader"}, "Course Title:"),
  431       CGI::td(CGI::textfield(-name=>"add_courseTitle", -value=>$add_courseTitle, -size=>25)),
  432     ),
  433     CGI::Tr({},
  434       CGI::th({class=>"LeftHeader"}, "Institution:"),
  435       CGI::td(CGI::textfield(-name=>"add_courseInstitution", -value=>$add_courseInstitution, -size=>25)),
  436     ),
  437   );
  438 
  439   print CGI::p("To add the WeBWorK administrators to the new course (as instructors) check the box below.");
  440   my @checked = ($add_admin_users) ?(checked=>1): ();  # workaround because CGI::checkbox seems to have a bug -- it won't default to checked.
  441   print CGI::p({},CGI::input({-type=>'checkbox', -name=>"add_admin_users", @checked }, "Add WeBWorK administrators to new course"));
  442 
  443   print CGI::p("To add an additional instructor to the new course, specify user information below. The user ID may contain only
  444   numbers, letters, hyphens, periods (dots), commas,and underscores.\n");
  445 
  446   print CGI::table({class=>"FormLayout"}, CGI::Tr({},
  447     CGI::td({},
  448       CGI::table({class=>"FormLayout"},
  449         CGI::Tr({},
  450           CGI::th({class=>"LeftHeader"}, "User ID:"),
  451           CGI::td(CGI::textfield(-name=>"add_initial_userID", -value=>$add_initial_userID, -size=>25)),
  452         ),
  453         CGI::Tr({},
  454           CGI::th({class=>"LeftHeader"}, "Password:"),
  455           CGI::td(CGI::password_field(-name=>"add_initial_password", -value=>$add_initial_password, -size=>25)),
  456         ),
  457         CGI::Tr({},
  458           CGI::th({class=>"LeftHeader"}, "Confirm Password:"),
  459           CGI::td(CGI::password_field(-name=>"add_initial_confirmPassword", -value=>$add_initial_confirmPassword, -size=>25)),
  460         ),
  461       ),
  462     ),
  463     CGI::td({},
  464       CGI::table({class=>"FormLayout"},
  465         CGI::Tr({},
  466           CGI::th({class=>"LeftHeader"}, "First Name:"),
  467           CGI::td(CGI::textfield(-name=>"add_initial_firstName", -value=>$add_initial_firstName, -size=>25)),
  468         ),
  469         CGI::Tr({},
  470           CGI::th({class=>"LeftHeader"}, "Last Name:"),
  471           CGI::td(CGI::textfield(-name=>"add_initial_lastName", -value=>$add_initial_lastName, -size=>25)),
  472         ),
  473         CGI::Tr({},
  474           CGI::th({class=>"LeftHeader"}, "Email Address:"),
  475           CGI::td(CGI::textfield(-name=>"add_initial_email", -value=>$add_initial_email, -size=>25)),
  476         ),
  477       ),
  478 
  479     ),
  480   ));
  481 
  482   print CGI::p("To copy problem templates from an existing course, select the course below.");
  483 
  484   print CGI::table({class=>"FormLayout"},
  485     CGI::Tr({},
  486       CGI::th({class=>"LeftHeader"}, "Copy templates from:"),
  487       CGI::td(
  488         CGI::popup_menu(
  489           -name => "add_templates_course",
  490           -values => [ "", @existingCourses ],
  491           -default => $add_templates_course,
  492           #-size => 10,
  493           #-multiple => 0,
  494           #-labels => \%courseLabels,
  495         ),
  496 
  497       ),
  498     ),
  499   );
  500 
  501 
  502 
  503   print CGI::p("Select a database layout below.");
  504   print CGI::start_table({class=>"FormLayout"});
  505 
  506   my %dbLayout_buttons;
  507   my $selected_dbLayout = defined $add_dbLayout ? $add_dbLayout : $ce->{dbLayout_order}[0];
  508   @dbLayout_buttons{@dbLayouts} = CGI::radio_group(-name=>"add_dbLayout",-values=>\@dbLayouts,-default=>$selected_dbLayout);
  509   foreach my $dbLayout (@dbLayouts) {
  510     my $dbLayoutLabel = (defined $ce->{dbLayout_descr}{$dbLayout})
  511       ? "$dbLayout - " . $ce->{dbLayout_descr}{$dbLayout}
  512       : "$dbLayout - no description provided in global.conf";
  513     print CGI::Tr({},
  514       CGI::td({width=>'20%'}, $dbLayout_buttons{$dbLayout}),
  515       CGI::td($dbLayoutLabel),
  516     );
  517   }
  518   print CGI::end_table();
  519   print CGI::p({style=>"text-align: left"}, CGI::submit(-name=>"add_course", -label=>"Add Course"));
  520 
  521   print CGI::end_form();
  522 }
  523 
  524 sub add_course_validate {
  525   my ($self) = @_;
  526   my $r = $self->r;
  527   my $ce = $r->ce;
  528   #my $db = $r->db;
  529   #my $authz = $r->authz;
  530   #my $urlpath = $r->urlpath;
  531 
  532   my $add_courseID                     = $r->param("add_courseID") || "";
  533   my $add_courseTitle                  = $r->param("add_courseTitle") || "";
  534   my $add_courseInstitution            = $r->param("add_courseInstitution") || "";
  535 
  536   my $add_admin_users                  = $r->param("add_admin_users") || "";
  537 
  538   my $add_initial_userID               = $r->param("add_initial_userID") || "";
  539   my $add_initial_password             = $r->param("add_initial_password") || "";
  540   my $add_initial_confirmPassword      = $r->param("add_initial_confirmPassword") || "";
  541   my $add_initial_firstName            = $r->param("add_initial_firstName") || "";
  542   my $add_initial_lastName             = $r->param("add_initial_lastName") || "";
  543   my $add_initial_email                = $r->param("add_initial_email") || "";
  544 
  545   my $add_templates_course             = $r->param("add_templates_course") || "";
  546 
  547   my $add_dbLayout                     = $r->param("add_dbLayout") || "";
  548 
  549   my @errors;
  550 
  551   if ($add_courseID eq "") {
  552     push @errors, "You must specify a course ID.";
  553   }
  554   unless ($add_courseID =~ /^[\w-]*$/) { # regex copied from CourseAdministration.pm
  555     push @errors, "Course ID may only contain letters, numbers, hyphens, and underscores.";
  556   }
  557   if (grep { $add_courseID eq $_ } listCourses($ce)) {
  558     push @errors, "A course with ID $add_courseID already exists.";
  559   }
  560   #if ($add_courseTitle eq "") {
  561   # push @errors, "You must specify a course title.";
  562   #}
  563   #if ($add_courseInstitution eq "") {
  564   # push @errors, "You must specify an institution for this course.";
  565   #}
  566 
  567   if ($add_initial_userID ne "") {
  568     if ($add_initial_password eq "") {
  569       push @errors, "You must specify a password for the initial instructor.";
  570     }
  571     if ($add_initial_confirmPassword eq "") {
  572       push @errors, "You must confirm the password for the initial instructor.";
  573     }
  574     if ($add_initial_password ne $add_initial_confirmPassword) {
  575       push @errors, "The password and password confirmation for the instructor must match.";
  576     }
  577     if ($add_initial_firstName eq "") {
  578       push @errors, "You must specify a first name for the initial instructor.";
  579     }
  580     if ($add_initial_lastName eq "") {
  581       push @errors, "You must specify a last name for the initial instructor.";
  582     }
  583     if ($add_initial_email eq "") {
  584       push @errors, "You must specify an email address for the initial instructor.";
  585     }
  586   }
  587 
  588   if ($add_dbLayout eq "") {
  589     push @errors, "You must select a database layout.";
  590   } else {
  591     if (exists $ce->{dbLayouts}->{$add_dbLayout}) {
  592       # we used to check for layout-specific fields here, but there aren't any layouts that require them
  593       # anymore. (in the future, we'll probably deal with this in layout-specific modules.)
  594     } else {
  595       push @errors, "The database layout $add_dbLayout doesn't exist.";
  596     }
  597   }
  598 
  599   return @errors;
  600 }
  601 
  602 sub do_add_course {
  603   my ($self) = @_;
  604   my $r = $self->r;
  605   my $ce = $r->ce;
  606   my $db = $r->db;
  607   my $authz = $r->authz;
  608   my $urlpath = $r->urlpath;
  609 
  610   my $add_courseID                     = $r->param("add_courseID") || "";
  611   my $add_courseTitle                  = $r->param("add_courseTitle") || "";
  612   my $add_courseInstitution            = $r->param("add_courseInstitution") || "";
  613 
  614   my $add_admin_users                  = $r->param("add_admin_users") || "";
  615 
  616   my $add_initial_userID               = $r->param("add_initial_userID") || "";
  617   my $add_initial_password             = $r->param("add_initial_password") || "";
  618   my $add_initial_confirmPassword      = $r->param("add_initial_confirmPassword") || "";
  619   my $add_initial_firstName            = $r->param("add_initial_firstName") || "";
  620   my $add_initial_lastName             = $r->param("add_initial_lastName") || "";
  621   my $add_initial_email                = $r->param("add_initial_email") || "";
  622 
  623   my $add_templates_course             = $r->param("add_templates_course") || "";
  624 
  625   my $add_dbLayout                     = $r->param("add_dbLayout") || "";
  626 
  627   my $ce2 = WeBWorK::CourseEnvironment->new(
  628     $ce->{webworkDirs}->{root},
  629     $ce->{webworkURLs}->{root},
  630     $ce->{pg}->{directories}->{root},
  631     $add_courseID,
  632   );
  633 
  634   my %courseOptions = ( dbLayoutName => $add_dbLayout );
  635 
  636   if ($add_initial_email ne "") {
  637     $courseOptions{allowedRecipients} = [ $add_initial_email ];
  638     # don't set feedbackRecipients -- this just gets in the way of the more
  639     # intelligent "receive_recipients" method.
  640     #$courseOptions{feedbackRecipients} = [ $add_initial_email ];
  641   }
  642 
  643   # this is kinda left over from when we had 'gdbm' and 'sql' database layouts
  644   # below this line, we would grab values from getopt and put them in this hash
  645   # but for now the hash can remain empty
  646   my %dbOptions;
  647 
  648   my @users;
  649 
  650   # copy users from current (admin) course if desired
  651   if ($add_admin_users ne "") {
  652     # DBFIXME do the searching in the database, and grab user/passwd/perm. all at once with a join
  653     foreach my $userID ($db->listUsers) {
  654       if ($userID eq $add_initial_userID) {
  655         $self->addbadmessage( "User '$userID' will not be copied from admin course as it is the initial instructor.");
  656         next;
  657       }
  658       my $User            = $db->getUser($userID);
  659       my $Password        = $db->getPassword($userID);
  660       my $PermissionLevel = $db->getPermissionLevel($userID);
  661       push @users, [ $User, $Password, $PermissionLevel ]
  662              if $authz->hasPermissions($userID,"create_and_delete_courses");
  663              #only transfer the "instructors" in the admin course classlist.
  664     }
  665   }
  666 
  667   # add initial instructor if desired
  668   if ($add_initial_userID ne "") {
  669     my $User = $db->newUser(
  670       user_id       => $add_initial_userID,
  671       first_name    => $add_initial_firstName,
  672       last_name     => $add_initial_lastName,
  673       student_id    => $add_initial_userID,
  674       email_address => $add_initial_email,
  675       status        => "C",
  676     );
  677     my $Password = $db->newPassword(
  678       user_id  => $add_initial_userID,
  679       password => cryptPassword($add_initial_password),
  680     );
  681     my $PermissionLevel = $db->newPermissionLevel(
  682       user_id    => $add_initial_userID,
  683       permission => "10",
  684     );
  685     push @users, [ $User, $Password, $PermissionLevel ];
  686   }
  687 
  688   push @{$courseOptions{PRINT_FILE_NAMES_FOR}}, map { $_->[0]->user_id } @users;
  689 
  690   my %optional_arguments;
  691   if ($add_templates_course ne "") {
  692     $optional_arguments{templatesFrom} = $add_templates_course;
  693   }
  694 
  695   eval {
  696     addCourse(
  697       courseID      => $add_courseID,
  698       ce            => $ce2,
  699       courseOptions => \%courseOptions,
  700       dbOptions     => \%dbOptions,
  701       users         => \@users,
  702       %optional_arguments,
  703     );
  704   };
  705   if ($@) {
  706     my $error = $@;
  707     print CGI::div({class=>"ResultsWithError"},
  708       CGI::p("An error occured while creating the course $add_courseID:"),
  709       CGI::tt(CGI::escapeHTML($error)),
  710     );
  711     # get rid of any partially built courses
  712     # FIXME  -- this is too fragile
  713     unless ($error =~ /course exists/) {
  714       eval {
  715         deleteCourse(
  716           courseID   => $add_courseID,
  717           ce         => $ce2,
  718           dbOptions  => \%dbOptions,
  719         );
  720       }
  721     }
  722   } else {
  723       #log the action
  724       writeLog($ce, "hosted_courses", join("\t",
  725         "\tAdded",
  726         ( defined $add_courseInstitution ? $add_courseInstitution : "(no institution specified)" ),
  727         ( defined $add_courseTitle ? $add_courseTitle : "(no title specified)" ),
  728         $add_courseID,
  729         $add_initial_firstName,
  730         $add_initial_lastName,
  731         $add_initial_email,
  732       ));
  733       # add contact to admin course as student?
  734       # FIXME -- should we do this?
  735       if ($add_initial_userID ne "") {
  736           my $composite_id = "${add_initial_userID}_${add_courseID}"; # student id includes school name and contact
  737       my $User = $db->newUser(
  738       user_id       => $composite_id,          # student id includes school name and contact
  739       first_name    => $add_initial_firstName,
  740       last_name     => $add_initial_lastName,
  741       student_id    => $add_initial_userID,
  742       email_address => $add_initial_email,
  743       status        => "C",
  744       );
  745       my $Password = $db->newPassword(
  746         user_id  => $composite_id,
  747         password => cryptPassword($add_initial_password),
  748       );
  749       my $PermissionLevel = $db->newPermissionLevel(
  750         user_id    => $composite_id,
  751         permission => "0",
  752       );
  753       # add contact to admin course as student
  754       # or if this contact and course already exist in a dropped status
  755       # change the student's status to enrolled
  756       # DBFIXME can use a REPLACE here?
  757       if (my $oldUser = $db->getUser($composite_id) ) {
  758         warn "Replacing old data for $composite_id  status: ". $oldUser->status;
  759         $db->deleteUser($composite_id);
  760       }
  761       eval { $db->addUser($User)                       }; warn $@ if $@;
  762       eval { $db->addPassword($Password)               }; warn $@ if $@;
  763       eval { $db->addPermissionLevel($PermissionLevel) }; warn $@ if $@;
  764     }
  765     print CGI::div({class=>"ResultsWithoutError"},
  766       CGI::p("Successfully created the course $add_courseID"),
  767     );
  768     my $newCoursePath = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets",
  769       courseID => $add_courseID);
  770     my $newCourseURL = $self->systemLink($newCoursePath, authen => 0);
  771     print CGI::div({style=>"text-align: center"},
  772       CGI::a({href=>$newCourseURL}, "Log into $add_courseID"),
  773     );
  774   }
  775 
  776 
  777 }
  778 
  779 ################################################################################
  780 
  781 sub rename_course_form {
  782   my ($self) = @_;
  783   my $r = $self->r;
  784   my $ce = $r->ce;
  785   #my $db = $r->db;
  786   #my $authz = $r->authz;
  787   #my $urlpath = $r->urlpath;
  788 
  789   my $rename_oldCourseID     = $r->param("rename_oldCourseID")     || "";
  790   my $rename_newCourseID     = $r->param("rename_newCourseID")     || "";
  791 
  792   my @courseIDs = listCourses($ce);
  793   @courseIDs    = sort {lc($a) cmp lc ($b) } @courseIDs;
  794 
  795   my %courseLabels; # records... heh.
  796   foreach my $courseID (@courseIDs) {
  797     my $tempCE = WeBWorK::CourseEnvironment->new(
  798       $ce->{webworkDirs}->{root},
  799       $ce->{webworkURLs}->{root},
  800       $ce->{pg}->{directories}->{root},
  801       $courseID,
  802     );
  803     $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
  804   }
  805 
  806   print CGI::h2("Rename Course");
  807 
  808   print CGI::start_form(-method=>"POST", -action=>$r->uri);
  809   print $self->hidden_authen_fields;
  810   print $self->hidden_fields("subDisplay");
  811 
  812   print CGI::p("Select a course to rename.");
  813 
  814   print CGI::table({class=>"FormLayout"},
  815     CGI::Tr({},
  816       CGI::th({class=>"LeftHeader"}, "Course Name:"),
  817       CGI::td(
  818         CGI::scrolling_list(
  819           -name => "rename_oldCourseID",
  820           -values => \@courseIDs,
  821           -default => $rename_oldCourseID,
  822           -size => 10,
  823           -multiple => 0,
  824           -labels => \%courseLabels,
  825         ),
  826       ),
  827     ),
  828     CGI::Tr({},
  829       CGI::th({class=>"LeftHeader"}, "New Name:"),
  830       CGI::td(CGI::textfield(-name=>"rename_newCourseID", -value=>$rename_newCourseID, -size=>25)),
  831     ),
  832   );
  833 
  834   print CGI::end_table();
  835 
  836   print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"rename_course", -label=>"Rename Course"));
  837 
  838   print CGI::end_form();
  839 }
  840 
  841 sub rename_course_validate {
  842   my ($self) = @_;
  843   my $r = $self->r;
  844   my $ce = $r->ce;
  845   #my $db = $r->db;
  846   #my $authz = $r->authz;
  847   #my $urlpath = $r->urlpath;
  848 
  849   my $rename_oldCourseID     = $r->param("rename_oldCourseID")     || "";
  850   my $rename_newCourseID     = $r->param("rename_newCourseID")     || "";
  851 
  852   my @errors;
  853 
  854   if ($rename_oldCourseID eq "") {
  855     push @errors, "You must select a course to rename.";
  856   }
  857   if ($rename_newCourseID eq "") {
  858     push @errors, "You must specify a new name for the course.";
  859   }
  860   if ($rename_oldCourseID eq $rename_newCourseID) {
  861     push @errors, "Can't rename to the same name.";
  862   }
  863   unless ($rename_newCourseID =~ /^[\w-]*$/) { # regex copied from CourseAdministration.pm
  864     push @errors, "Course ID may only contain letters, numbers, hyphens, and underscores.";
  865   }
  866   if (grep { $rename_newCourseID eq $_ } listCourses($ce)) {
  867     push @errors, "A course with ID $rename_newCourseID already exists.";
  868   }
  869 
  870   my $ce2 = WeBWorK::CourseEnvironment->new(
  871     $ce->{webworkDirs}->{root},
  872     $ce->{webworkURLs}->{root},
  873     $ce->{pg}->{directories}->{root},
  874     $rename_oldCourseID,
  875   );
  876 
  877   return @errors;
  878 }
  879 
  880 sub do_rename_course {
  881   my ($self) = @_;
  882   my $r = $self->r;
  883   my $ce = $r->ce;
  884   my $db = $r->db;
  885   #my $authz = $r->authz;
  886   my $urlpath = $r->urlpath;
  887 
  888   my $rename_oldCourseID     = $r->param("rename_oldCourseID")     || "";
  889   my $rename_newCourseID     = $r->param("rename_newCourseID")     || "";
  890 
  891   my $ce2 = WeBWorK::CourseEnvironment->new(
  892     $ce->{webworkDirs}->{root},
  893     $ce->{webworkURLs}->{root},
  894     $ce->{pg}->{directories}->{root},
  895     $rename_oldCourseID,
  896   );
  897 
  898   my $dbLayoutName = $ce->{dbLayoutName};
  899 
  900   # this is kinda left over from when we had 'gdbm' and 'sql' database layouts
  901   # below this line, we would grab values from getopt and put them in this hash
  902   # but for now the hash can remain empty
  903   my %dbOptions;
  904 
  905   eval {
  906     renameCourse(
  907       courseID      => $rename_oldCourseID,
  908       ce            => $ce2,
  909       dbOptions     => \%dbOptions,
  910       newCourseID   => $rename_newCourseID,
  911     );
  912   };
  913   if ($@) {
  914     my $error = $@;
  915     print CGI::div({class=>"ResultsWithError"},
  916       CGI::p("An error occured while renaming the course $rename_oldCourseID to $rename_newCourseID:"),
  917       CGI::tt(CGI::escapeHTML($error)),
  918     );
  919   } else {
  920     print CGI::div({class=>"ResultsWithoutError"},
  921       CGI::p("Successfully renamed the course $rename_oldCourseID to $rename_newCourseID"),
  922     );
  923     my $newCoursePath = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets",
  924       courseID => $rename_newCourseID);
  925     my $newCourseURL = $self->systemLink($newCoursePath, authen => 0);
  926     print CGI::div({style=>"text-align: center"},
  927       CGI::a({href=>$newCourseURL}, "Log into $rename_newCourseID"),
  928     );
  929   }
  930 }
  931 
  932 ################################################################################
  933 
  934 sub delete_course_form {
  935   my ($self) = @_;
  936   my $r = $self->r;
  937   my $ce = $r->ce;
  938   #my $db = $r->db;
  939   #my $authz = $r->authz;
  940   #my $urlpath = $r->urlpath;
  941 
  942   my $delete_courseID     = $r->param("delete_courseID")     || "";
  943 
  944   my @courseIDs = listCourses($ce);
  945   @courseIDs    = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive
  946 
  947   my %courseLabels; # records... heh.
  948   foreach my $courseID (@courseIDs) {
  949     my $tempCE = WeBWorK::CourseEnvironment->new(
  950       $ce->{webworkDirs}->{root},
  951       $ce->{webworkURLs}->{root},
  952       $ce->{pg}->{directories}->{root},
  953       $courseID,
  954     );
  955     $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
  956   }
  957 
  958   print CGI::h2("Delete Course");
  959 
  960   print CGI::start_form(-method=>"POST", -action=>$r->uri);
  961   print $self->hidden_authen_fields;
  962   print $self->hidden_fields("subDisplay");
  963 
  964   print CGI::p("Select a course to delete.");
  965 
  966   print CGI::table({class=>"FormLayout"},
  967     CGI::Tr({},
  968       CGI::th({class=>"LeftHeader"}, "Course Name:"),
  969       CGI::td(
  970         CGI::scrolling_list(
  971           -name => "delete_courseID",
  972           -values => \@courseIDs,
  973           -default => $delete_courseID,
  974           -size => 10,
  975           -multiple => 0,
  976           -labels => \%courseLabels,
  977         ),
  978       ),
  979     ),
  980   );
  981 
  982   print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"delete_course", -value=>"Delete Course"));
  983 
  984   print CGI::end_form();
  985 }
  986 
  987 sub delete_course_validate {
  988   my ($self) = @_;
  989   my $r = $self->r;
  990   my $ce = $r->ce;
  991   #my $db = $r->db;
  992   #my $authz = $r->authz;
  993   my $urlpath = $r->urlpath;
  994 
  995   my $delete_courseID     = $r->param("delete_courseID")     || "";
  996 
  997   my @errors;
  998 
  999   if ($delete_courseID eq "") {
 1000     push @errors, "You must specify a course name.";
 1001   } elsif ($delete_courseID eq $urlpath->arg("courseID")) {
 1002     push @errors, "You cannot delete the course you are currently using.";
 1003   }
 1004 
 1005   my $ce2 = WeBWorK::CourseEnvironment->new(
 1006     $ce->{webworkDirs}->{root},
 1007     $ce->{webworkURLs}->{root},
 1008     $ce->{pg}->{directories}->{root},
 1009     $delete_courseID,
 1010   );
 1011 
 1012   return @errors;
 1013 }
 1014 
 1015 sub delete_course_confirm {
 1016   my ($self) = @_;
 1017   my $r = $self->r;
 1018   my $ce = $r->ce;
 1019   #my $db = $r->db;
 1020   #my $authz = $r->authz;
 1021   #my $urlpath = $r->urlpath;
 1022 
 1023   print CGI::h2("Delete Course");
 1024 
 1025   my $delete_courseID     = $r->param("delete_courseID")     || "";
 1026 
 1027   my $ce2 = WeBWorK::CourseEnvironment->new(
 1028     $ce->{webworkDirs}->{root},
 1029     $ce->{webworkURLs}->{root},
 1030     $ce->{pg}->{directories}->{root},
 1031     $delete_courseID,
 1032   );
 1033 
 1034   print CGI::p("Are you sure you want to delete the course " . CGI::b($delete_courseID)
 1035     . "? All course files and data will be destroyed. There is no undo available.");
 1036 
 1037   print CGI::start_form(-method=>"POST", -action=>$r->uri);
 1038   print $self->hidden_authen_fields;
 1039   print $self->hidden_fields("subDisplay");
 1040   print $self->hidden_fields(qw/delete_courseID/);
 1041 
 1042   print CGI::p({style=>"text-align: center"},
 1043     CGI::submit(-name=>"decline_delete_course", -label=>"Don't delete"),
 1044     "&nbsp;",
 1045     CGI::submit(-name=>"confirm_delete_course", -label=>"Delete"),
 1046   );
 1047 
 1048   print CGI::end_form();
 1049 }
 1050 
 1051 sub do_delete_course {
 1052   my ($self) = @_;
 1053   my $r = $self->r;
 1054   my $ce = $r->ce;
 1055   my $db = $r->db;
 1056   #my $authz = $r->authz;
 1057   #my $urlpath = $r->urlpath;
 1058 
 1059   my $delete_courseID     = $r->param("delete_courseID")     || "";
 1060 
 1061   my $ce2 = WeBWorK::CourseEnvironment->new(
 1062     $ce->{webworkDirs}->{root},
 1063     $ce->{webworkURLs}->{root},
 1064     $ce->{pg}->{directories}->{root},
 1065     $delete_courseID,
 1066   );
 1067 
 1068   # this is kinda left over from when we had 'gdbm' and 'sql' database layouts
 1069   # below this line, we would grab values from getopt and put them in this hash
 1070   # but for now the hash can remain empty
 1071   my %dbOptions;
 1072 
 1073   eval {
 1074     deleteCourse(
 1075       courseID => $delete_courseID,
 1076       ce => $ce2,
 1077       dbOptions => \%dbOptions,
 1078     );
 1079   };
 1080 
 1081   if ($@) {
 1082     my $error = $@;
 1083     print CGI::div({class=>"ResultsWithError"},
 1084       CGI::p("An error occured while deleting the course $delete_courseID:"),
 1085       CGI::tt(CGI::escapeHTML($error)),
 1086     );
 1087   } else {
 1088       # mark the contact person in the admin course as dropped.
 1089       # find the contact person for the course by searching the admin classlist.
 1090       # DBFIXME use a where clause, iterator
 1091       my @contacts = grep /_$delete_courseID$/,  $db->listUsers;
 1092       if (@contacts) {
 1093       die "Incorrect number of contacts for the course $delete_courseID". join(" ", @contacts) if @contacts !=1;
 1094       #warn "contacts", join(" ", @contacts);
 1095       #my $composite_id = "${add_initial_userID}_${add_courseID}";
 1096       my $composite_id  = $contacts[0];
 1097 
 1098       # mark the contact person as dropped.
 1099       my $User = $db->getUser($composite_id);
 1100       my $status_name = 'Drop';
 1101       my $status_value = ($ce->status_name_to_abbrevs($status_name))[0];
 1102       $User->status($status_value);
 1103       $db->putUser($User);
 1104     }
 1105 
 1106     print CGI::div({class=>"ResultsWithoutError"},
 1107       CGI::p("Successfully deleted the course $delete_courseID."),
 1108     );
 1109      writeLog($ce, "hosted_courses", join("\t",
 1110         "\tDeleted",
 1111         "",
 1112         "",
 1113         $delete_courseID,
 1114       ));
 1115     print CGI::start_form(-method=>"POST", -action=>$r->uri);
 1116     print $self->hidden_authen_fields;
 1117     print $self->hidden_fields("subDisplay");
 1118 
 1119     print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"decline_delete_course", -value=>"OK"),);
 1120 
 1121     print CGI::end_form();
 1122   }
 1123 }
 1124 
 1125 ################################################################################
 1126 
 1127 sub export_database_form {
 1128   my ($self) = @_;
 1129   my $r = $self->r;
 1130   my $ce = $r->ce;
 1131   #my $db = $r->db;
 1132   #my $authz = $r->authz;
 1133   #my $urlpath = $r->urlpath;
 1134 
 1135   my @tables = keys %{$ce->{dbLayout}};
 1136 
 1137   my $export_courseID = $r->param("export_courseID") || "";
 1138   my @export_tables   = $r->param("export_tables");
 1139 
 1140   @export_tables = @tables unless @export_tables;
 1141 
 1142   my @courseIDs = listCourses($ce);
 1143   @courseIDs    = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive
 1144 
 1145   my %courseLabels; # records... heh.
 1146   foreach my $courseID (@courseIDs) {
 1147     my $tempCE = WeBWorK::CourseEnvironment->new(
 1148       $ce->{webworkDirs}->{root},
 1149       $ce->{webworkURLs}->{root},
 1150       $ce->{pg}->{directories}->{root},
 1151       $courseID,
 1152     );
 1153     $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
 1154   }
 1155 
 1156   print CGI::h2("Export Database");
 1157 
 1158   print CGI::p(IMPORT_EXPORT_WARNING);
 1159 
 1160   print CGI::start_form(-method=>"GET", -action=>$r->uri);
 1161   print $self->hidden_authen_fields;
 1162   print $self->hidden_fields("subDisplay");
 1163 
 1164   print CGI::p({},"Select a course to export the course's database. Please note
 1165   that exporting can take a very long time for a large course. If you have
 1166   shell access to the WeBWorK server, you may use the ", CGI::code("wwdb"), "
 1167   utility instead.");
 1168 
 1169   print CGI::table({class=>"FormLayout"},
 1170     CGI::Tr({},
 1171       CGI::th({class=>"LeftHeader"}, "Course Name:"),
 1172       CGI::td(
 1173         CGI::scrolling_list(
 1174           -name => "export_courseID",
 1175           -values => \@courseIDs,
 1176           -default => $export_courseID,
 1177           -size => 10,
 1178           -multiple => 1,
 1179           -labels => \%courseLabels,
 1180         ),
 1181       ),
 1182     ),
 1183     CGI::Tr({},
 1184       CGI::th({class=>"LeftHeader"}, "Tables to Export:"),
 1185       CGI::td({},
 1186         CGI::checkbox_group(
 1187           -name => "export_tables",
 1188           -values => \@tables,
 1189           -default => \@export_tables,
 1190           -linebreak => 1,
 1191         ),
 1192       ),
 1193     ),
 1194   );
 1195 
 1196   print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"export_database", -value=>"Export Database"));
 1197 
 1198   print CGI::end_form();
 1199 }
 1200 
 1201 sub export_database_validate {
 1202   my ($self) = @_;
 1203   my $r = $self->r;
 1204   #my $ce = $r->ce;
 1205   #my $db = $r->db;
 1206   #my $authz = $r->authz;
 1207   #my $urlpath = $r->urlpath;
 1208 
 1209   my @export_courseID = $r->param("export_courseID") || ();
 1210   my @export_tables   = $r->param("export_tables");
 1211 
 1212   my @errors;
 1213 
 1214   unless ( @export_courseID) {
 1215     push @errors, "You must specify at least one course name.";
 1216   }
 1217 
 1218   unless (@export_tables) {
 1219     push @errors, "You must specify at least one table to export.";
 1220   }
 1221 
 1222   return @errors;
 1223 }
 1224 
 1225 sub do_export_database {
 1226   my ($self) = @_;
 1227   my $r = $self->r;
 1228   my $ce = $r->ce;
 1229   #my $db = $r->db;
 1230   #my $authz = $r->authz;
 1231   my $urlpath = $r->urlpath;
 1232 
 1233   my @export_courseID = $r->param("export_courseID");
 1234   my @export_tables   = $r->param("export_tables");
 1235 
 1236   foreach my $export_courseID (@export_courseID) {
 1237 
 1238     my $ce2 = WeBWorK::CourseEnvironment->new(
 1239       $ce->{webworkDirs}->{root},
 1240       $ce->{webworkURLs}->{root},
 1241       $ce->{pg}->{directories}->{root},
 1242       $export_courseID,
 1243     );
 1244 
 1245     my $db2 = new WeBWorK::DB($ce2->{dbLayout});
 1246 
 1247     #my ($fh, $export_file) = tempfile("db_export_XXXXXX", DIR => $ce->{webworkDirs}->{tmp});
 1248     #my ($random_chars) = $export_file =~ m/db_export_(\w+)$/;
 1249     # export to the admin/templates directory
 1250     my $exportFileName = "$export_courseID.exported.xml";
 1251     my $exportFilePath = $ce->{courseDirs}->{templates}."/$exportFileName";
 1252     # get a unique name
 1253     my $number =1;
 1254     while (-e "$exportFilePath.$number.gz") {
 1255       $number++;
 1256       last if $number>9;
 1257     }
 1258     if ($number<=9 ) {
 1259       $exportFilePath = "$exportFilePath.$number";
 1260       $exportFileName = "$exportFileName.$number";
 1261     } else {
 1262       $self->addbadmessage(CGI::p("There are more than 9 exported files for this course! Please
 1263       remove some of these files."));
 1264       $exportFilePath = "$exportFilePath.999";
 1265       $exportFileName = "$exportFileName.999";
 1266     }
 1267 
 1268     my $outputFileHandle = new IO::File(">$exportFilePath") or warn "Unable to create $exportFilePath";
 1269 
 1270     my @errors;
 1271     eval {
 1272       @errors = dbExport(
 1273         db => $db2,
 1274         #xml => $fh,
 1275         xml => $outputFileHandle,
 1276         tables => \@export_tables,
 1277       );
 1278     };
 1279 
 1280     $outputFileHandle->close();
 1281 
 1282     my $gzipMessage = system( 'gzip', $exportFilePath);
 1283     if ( !$gzipMessage ) {
 1284       $self->addgoodmessage(CGI::p( "Database saved to templates/$exportFileName.gzip.
 1285       You may download it with the file manager."));
 1286     } else {
 1287       $self->addbadmessage(CGI::p( "Failed to gzip file $exportFilePath"));
 1288     }
 1289     unlink $exportFilePath;
 1290   } # end export of one course
 1291   #push @errors, "Fatal exception: $@" if $@;
 1292   #
 1293   #if (@errors) {
 1294   # print CGI::div({class=>"ResultsWithError"},
 1295   #   CGI::p("An error occured while exporting the database of course $export_courseID:"),
 1296   #   CGI::ul(CGI::li(\@errors)),
 1297   # );
 1298   #} else {
 1299   # print CGI::div({class=>"ResultsWithoutError"},
 1300   #   CGI::p("Export succeeded."),
 1301   # );
 1302   #
 1303   # print CGI::div({style=>"text-align: center"},
 1304   #   CGI::a({href=>$self->systemLink($urlpath, params=>{download_exported_database=>$random_chars, export_courseID=>undef})}, "Download Exported Database"),
 1305   # );
 1306   #}
 1307 }
 1308 
 1309 ################################################################################
 1310 
 1311 sub import_database_form {
 1312   my ($self) = @_;
 1313   my $r = $self->r;
 1314   my $ce = $r->ce;
 1315   #my $db = $r->db;
 1316   #my $authz = $r->authz;
 1317   #my $urlpath = $r->urlpath;
 1318 
 1319   my @tables = keys %{$ce->{dbLayout}};
 1320 
 1321   my $import_file     = $r->param("import_file")     || "";
 1322   my $import_courseID = $r->param("import_courseID") || "";
 1323   my @import_tables   = $r->param("import_tables");
 1324   my $import_conflict = $r->param("import_conflict") || "skip";
 1325 
 1326   @import_tables = @tables unless @import_tables;
 1327 
 1328   my @courseIDs = listCourses($ce);
 1329   @courseIDs    = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive
 1330 
 1331 
 1332   my %courseLabels; # records... heh.
 1333   foreach my $courseID (@courseIDs) {
 1334     my $tempCE = WeBWorK::CourseEnvironment->new(
 1335       $ce->{webworkDirs}->{root},
 1336       $ce->{webworkURLs}->{root},
 1337       $ce->{pg}->{directories}->{root},
 1338       $courseID,
 1339     );
 1340     $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
 1341   }
 1342 
 1343   # find databases:
 1344   my $templatesDir = $ce->{courseDirs}->{templates};
 1345   my %probLibs = %{ $r->ce->{courseFiles}->{problibs} };
 1346   my $exempt_dirs = join("|", keys %probLibs);
 1347 
 1348   my @databaseFiles = listFilesRecursive(
 1349     $templatesDir,
 1350     qr/.\.exported\.xml\.\d*\.gz$/, # match these files  #FIXME this is too restricive!!
 1351     qr/^(?:$exempt_dirs|CVS)$/, # prune these directories
 1352     0, # match against file name only
 1353     1, # prune against path relative to $templatesDir
 1354   );
 1355 
 1356   my %databaseLabels = map { ($_ => $_) } @databaseFiles;
 1357 
 1358   #######
 1359 
 1360   print CGI::h2("Import Database");
 1361 
 1362   print CGI::p(IMPORT_EXPORT_WARNING);
 1363 
 1364   print CGI::start_form(-method=>"POST", -action=>$r->uri, -enctype=>&CGI::MULTIPART);
 1365   print $self->hidden_authen_fields;
 1366   print $self->hidden_fields("subDisplay");
 1367 
 1368   print CGI::table({class=>"FormLayout"},
 1369     CGI::Tr({},
 1370       CGI::th({class=>"LeftHeader"}, "Database XML File:"),
 1371       CGI::td(
 1372         CGI::scrolling_list(
 1373           -name => "import_file",
 1374           -values => \@databaseFiles,
 1375           -default => undef,
 1376           -size => 10,
 1377           -multiple => 0,
 1378           -labels => \%databaseLabels,
 1379         ),
 1380 
 1381       )
 1382     ),
 1383     CGI::Tr({},
 1384       CGI::th({class=>"LeftHeader"}, "Tables to Import:"),
 1385       CGI::td(
 1386         CGI::checkbox_group(
 1387           -name => "import_tables",
 1388           -values => \@tables,
 1389           -default => \@import_tables,
 1390           -linebreak => 1,
 1391         ),
 1392       ),
 1393     ),
 1394     CGI::Tr({},
 1395       CGI::th({class=>"LeftHeader"}, "Import into Course:"),
 1396       CGI::td(
 1397         CGI::scrolling_list(
 1398           -name => "import_courseID",
 1399           -values => \@courseIDs,
 1400           -default => $import_courseID,
 1401           -size => 10,
 1402           -multiple => 0,
 1403           -labels => \%courseLabels,
 1404         ),
 1405       ),
 1406     ),
 1407     CGI::Tr({},
 1408       CGI::th({class=>"LeftHeader"}, "Conflicts:"),
 1409       CGI::td(
 1410         CGI::radio_group(
 1411           -name => "import_conflict",
 1412           -values => [qw/skip replace/],
 1413           -default => $import_conflict,
 1414           -linebreak=>'true',
 1415           -labels => {
 1416             skip => "Skip duplicate records",
 1417             replace => "Replace duplicate records",
 1418           },
 1419         ),
 1420       ),
 1421     ),
 1422   );
 1423 
 1424   print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"import_database", -value=>"Import Database"));
 1425 
 1426   print CGI::end_form();
 1427 }
 1428 
 1429 sub import_database_validate {
 1430   my ($self) = @_;
 1431   my $r = $self->r;
 1432   #my $ce = $r->ce;
 1433   #my $db = $r->db;
 1434   #my $authz = $r->authz;
 1435   #my $urlpath = $r->urlpath;
 1436 
 1437   my $import_file     = $r->param("import_file")     || "";
 1438   my $import_courseID = $r->param("import_courseID") || "";
 1439   my @import_tables   = $r->param("import_tables");
 1440   #my $import_conflict = $r->param("import_conflict") || "skip"; # not checked
 1441 
 1442   my @errors;
 1443 
 1444   if ($import_file eq "") {
 1445     push @errors, "You must specify a database file to import.";
 1446   }
 1447 
 1448   if ($import_courseID eq "") {
 1449     push @errors, "You must specify a course name.";
 1450   }
 1451 
 1452   unless (@import_tables) {
 1453     push @errors, "You must specify at least one table to import.";
 1454   }
 1455 
 1456   return @errors;
 1457 }
 1458 
 1459 sub do_import_database {
 1460   my ($self) = @_;
 1461   my $r = $self->r;
 1462   my $ce = $r->ce;
 1463   #my $db = $r->db;
 1464   #my $authz = $r->authz;
 1465   my $urlpath = $r->urlpath;
 1466 
 1467   my $import_file     = $r->param("import_file");
 1468   my $import_courseID = $r->param("import_courseID");
 1469   my @import_tables   = $r->param("import_tables");
 1470   my $import_conflict = $r->param("import_conflict") || "skip"; # need default -- not checked above
 1471 
 1472   my $ce2 = WeBWorK::CourseEnvironment->new(
 1473     $ce->{webworkDirs}->{root},
 1474     $ce->{webworkURLs}->{root},
 1475     $ce->{pg}->{directories}->{root},
 1476     $import_courseID,
 1477   );
 1478 
 1479   my $db2 = new WeBWorK::DB($ce2->{dbLayout});
 1480 
 1481   # locate file
 1482   my $templateDir = $ce->{courseDirs}->{templates};
 1483   my $filePath = "$templateDir/$import_file";
 1484 
 1485   my $gunzipMessage = system( 'gunzip', $filePath);
 1486   #FIXME
 1487   #warn "gunzip ", $gunzipMessage;
 1488   $filePath =~ s/\.gz$//;
 1489   #warn "new file path is $filePath";
 1490   my $fileHandle = new IO::File("<$filePath");
 1491   # retrieve upload from upload cache
 1492 #   my ($id, $hash) = split /\s+/, $import_file;
 1493 #   my $upload = WeBWorK::Upload->retrieve($id, $hash,
 1494 #     dir => $ce->{webworkDirs}->{uploadCache}
 1495 #   );
 1496 
 1497   my @errors;
 1498 
 1499   eval {
 1500     @errors = dbImport(
 1501       db => $db2,
 1502       # xml => $upload->fileHandle,
 1503       xml => $fileHandle,
 1504       tables => \@import_tables,
 1505       conflict => $import_conflict,
 1506     );
 1507   };
 1508 
 1509   push @errors, "Fatal exception: $@" if $@;
 1510   push @errors, $gunzipMessage if $gunzipMessage;
 1511 
 1512   if (@errors) {
 1513     print CGI::div({class=>"ResultsWithError"},
 1514       CGI::p("An error occured while importing the database of course $import_courseID:"),
 1515       CGI::ul(CGI::li(\@errors)),
 1516     );
 1517   } else {
 1518     print CGI::div({class=>"ResultsWithoutError"},
 1519       CGI::p("Import succeeded."),
 1520     );
 1521   }
 1522 }
 1523 ##########################################################################
 1524 sub archive_course_form {
 1525   my ($self) = @_;
 1526   my $r = $self->r;
 1527   my $ce = $r->ce;
 1528   #my $db = $r->db;
 1529   #my $authz = $r->authz;
 1530   #my $urlpath = $r->urlpath;
 1531 
 1532   my $archive_courseID     = $r->param("archive_courseID")     || "";
 1533 
 1534   my @courseIDs = listCourses($ce);
 1535   @courseIDs    = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive
 1536 
 1537   my %courseLabels; # records... heh.
 1538   foreach my $courseID (@courseIDs) {
 1539     my $tempCE = WeBWorK::CourseEnvironment->new(
 1540       $ce->{webworkDirs}->{root},
 1541       $ce->{webworkURLs}->{root},
 1542       $ce->{pg}->{directories}->{root},
 1543       $courseID,
 1544     );
 1545     $courseLabels{$courseID} = "$courseID (" . $tempCE->{dbLayoutName} . ")";
 1546   }
 1547 
 1548   print CGI::h2("archive Course");
 1549 
 1550   print CGI::start_form(-method=>"POST", -action=>$r->uri);
 1551   print $self->hidden_authen_fields;
 1552   print $self->hidden_fields("subDisplay");
 1553 
 1554   print CGI::p("Select a course to archive.");
 1555 
 1556   print CGI::table({class=>"FormLayout"},
 1557     CGI::Tr({},
 1558       CGI::th({class=>"LeftHeader"}, "Course Name:"),
 1559       CGI::td(
 1560         CGI::scrolling_list(
 1561           -name => "archive_courseID",
 1562           -values => \@courseIDs,
 1563           -default => $archive_courseID,
 1564           -size => 10,
 1565           -multiple => 0,
 1566           -labels => \%courseLabels,
 1567         ),
 1568       ),
 1569 
 1570     ),
 1571     CGI::Tr({},
 1572       CGI::th({class=>"LeftHeader"}, "Delete course:"),
 1573       CGI::td({-style=>'color:red'}, CGI::checkbox({
 1574                           -name=>'delete_course',
 1575                           -checked=>0,
 1576                           -value => 1,
 1577                           -label =>'Delete course after archiving. Caution there is no undo!',
 1578                          },
 1579              ),
 1580       ),
 1581     )
 1582   );
 1583 
 1584   print CGI::p(
 1585     "Currently the archive facility is only available for mysql databases.
 1586     It depends on the mysqldump application."
 1587   );
 1588 
 1589 
 1590   print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"archive_course", -value=>"archive Course"));
 1591 
 1592   print CGI::end_form();
 1593 }
 1594 
 1595 sub archive_course_validate {
 1596   my ($self) = @_;
 1597   my $r = $self->r;
 1598   my $ce = $r->ce;
 1599   #my $db = $r->db;
 1600   #my $authz = $r->authz;
 1601   my $urlpath = $r->urlpath;
 1602 
 1603   my $archive_courseID     = $r->param("archive_courseID")     || "";
 1604 
 1605   my @errors;
 1606 
 1607   if ($archive_courseID eq "") {
 1608     push @errors, "You must specify a course name.";
 1609   } elsif ($archive_courseID eq $urlpath->arg("courseID")) {
 1610     push @errors, "You cannot archive the course you are currently using.";
 1611   }
 1612 
 1613   #my $ce2 = WeBWorK::CourseEnvironment->new(
 1614   # $ce->{webworkDirs}->{root},
 1615   # $ce->{webworkURLs}->{root},
 1616   # $ce->{pg}->{directories}->{root},
 1617   # $archive_courseID,
 1618   #);
 1619 
 1620   return @errors;
 1621 }
 1622 
 1623 sub archive_course_confirm {
 1624   my ($self) = @_;
 1625   my $r = $self->r;
 1626   my $ce = $r->ce;
 1627   #my $db = $r->db;
 1628   #my $authz = $r->authz;
 1629   #my $urlpath = $r->urlpath;
 1630 
 1631   print CGI::h2("archive Course");
 1632 
 1633   my $archive_courseID     = $r->param("archive_courseID")     || "";
 1634   my $delete_course_flag   = $r->param("delete_course")        || "";
 1635 
 1636   my $ce2 = WeBWorK::CourseEnvironment->new(
 1637     $ce->{webworkDirs}->{root},
 1638     $ce->{webworkURLs}->{root},
 1639     $ce->{pg}->{directories}->{root},
 1640     $archive_courseID,
 1641   );
 1642 
 1643   if ($ce2->{dbLayoutName} ) {
 1644     print CGI::p("Are you sure you want to archive the course " . CGI::b($archive_courseID)
 1645     . "? ");
 1646     print(CGI::p({-style=>'color:red; font-weight:bold'}, "Are you sure that you want to delete the course ".
 1647     CGI::b($archive_courseID). " after archiving?  This cannot be undone!")) if $delete_course_flag;
 1648 
 1649 
 1650   }
 1651 
 1652   print CGI::start_form(-method=>"POST", -action=>$r->uri);
 1653   print $self->hidden_authen_fields;
 1654   print $self->hidden_fields("subDisplay");
 1655   print $self->hidden_fields(qw/archive_courseID delete_course/);
 1656 
 1657   print CGI::p({style=>"text-align: center"},
 1658     CGI::submit(-name=>"decline_archive_course", -value=>"Don't archive"),
 1659     "&nbsp;",
 1660     CGI::submit(-name=>"confirm_archive_course", -value=>"archive"),
 1661   );
 1662 
 1663   print CGI::end_form();
 1664 }
 1665 
 1666 sub do_archive_course {
 1667   my ($self) = @_;
 1668   my $r = $self->r;
 1669   my $ce = $r->ce;
 1670   my $db = $r->db;
 1671   #my $authz = $r->authz;
 1672   #my $urlpath = $r->urlpath;
 1673 
 1674   my $archive_courseID     = $r->param("archive_courseID")     || "";
 1675   my $delete_course_flag   = $r->param("delete_course")        || "";
 1676 
 1677   my $ce2 = WeBWorK::CourseEnvironment->new(
 1678     $ce->{webworkDirs}->{root},
 1679     $ce->{webworkURLs}->{root},
 1680     $ce->{pg}->{directories}->{root},
 1681     $archive_courseID,
 1682   );
 1683 
 1684   # this is kinda left over from when we had 'gdbm' and 'sql' database layouts
 1685   # below this line, we would grab values from getopt and put them in this hash
 1686   # but for now the hash can remain empty
 1687   my %dbOptions;
 1688 
 1689   eval {
 1690     archiveCourse(
 1691       courseID => $archive_courseID,
 1692       ce => $ce2,
 1693       dbOptions => \%dbOptions,
 1694     );
 1695   };
 1696 
 1697   if ($@) {
 1698     my $error = $@;
 1699     print CGI::div({class=>"ResultsWithError"},
 1700       CGI::p("An error occured while archiving the course $archive_courseID:"),
 1701       CGI::tt(CGI::escapeHTML($error)),
 1702     );
 1703   } else {
 1704     print CGI::div({class=>"ResultsWithoutError"},
 1705       CGI::p("Successfully archived the course $archive_courseID"),
 1706     );
 1707      writeLog($ce, "hosted_courses", join("\t",
 1708         "\tarchived",
 1709         "",
 1710         "",
 1711         $archive_courseID,
 1712       ));
 1713 
 1714     if ($delete_course_flag) {
 1715       eval {
 1716         deleteCourse(
 1717           courseID => $archive_courseID,
 1718           ce => $ce2,
 1719           dbOptions => \%dbOptions,
 1720         );
 1721       };
 1722 
 1723       if ($@) {
 1724         my $error = $@;
 1725         print CGI::div({class=>"ResultsWithError"},
 1726           CGI::p("An error occured while deleting the course $archive_courseID:"),
 1727           CGI::tt(CGI::escapeHTML($error)),
 1728         );
 1729       } else {
 1730         # mark the contact person in the admin course as dropped.
 1731         # find the contact person for the course by searching the admin classlist.
 1732         # DBFIXME where clause, iterator
 1733         my @contacts = grep /_$archive_courseID$/,  $db->listUsers;
 1734         if (@contacts) {
 1735           die "Incorrect number of contacts for the course $archive_courseID". join(" ", @contacts) if @contacts !=1;
 1736           #warn "contacts", join(" ", @contacts);
 1737           #my $composite_id = "${add_initial_userID}_${add_courseID}";
 1738           my $composite_id  = $contacts[0];
 1739 
 1740           # mark the contact person as dropped.
 1741           my $User = $db->getUser($composite_id);
 1742           my $status_name = 'Drop';
 1743           my $status_value = ($ce->status_name_to_abbrevs($status_name))[0];
 1744           $User->status($status_value);
 1745           $db->putUser($User);
 1746         }
 1747 
 1748         print CGI::div({class=>"ResultsWithoutError"},
 1749           CGI::p("Successfully deleted the course $archive_courseID."),
 1750         );
 1751       }
 1752 
 1753 
 1754     }
 1755 
 1756 #     print CGI::start_form(-method=>"POST", -action=>$r->uri);
 1757 #     print $self->hidden_authen_fields;
 1758 #     print $self->hidden_fields("subDisplay");
 1759 #
 1760 #     print CGI::p({style=>"text-align: center"}, CGI::submit("decline_archive_course", "OK"),);
 1761 #
 1762 #     print CGI::end_form();
 1763   }
 1764 }
 1765 ##########################################################################
 1766 sub unarchive_course_form {
 1767   my ($self) = @_;
 1768   my $r = $self->r;
 1769   my $ce = $r->ce;
 1770   #my $db = $r->db;
 1771   #my $authz = $r->authz;
 1772   #my $urlpath = $r->urlpath;
 1773 
 1774   my $unarchive_courseID     = $r->param("unarchive_courseID")     || "";
 1775 
 1776   # First find courses which have been archived.
 1777   my @courseIDs = listArchivedCourses($ce);
 1778   @courseIDs    = sort {lc($a) cmp lc ($b) } @courseIDs; #make sort case insensitive
 1779 
 1780   my %courseLabels; # records... heh.
 1781   foreach my $courseID (@courseIDs) {
 1782         $courseLabels{$courseID} = $courseID;
 1783   }
 1784 
 1785   print CGI::h2("Unarchive Course -- not yet operational");
 1786 
 1787   print CGI::start_form(-method=>"POST", -action=>$r->uri);
 1788   print $self->hidden_authen_fields;
 1789   print $self->hidden_fields("subDisplay");
 1790 
 1791   print CGI::p("Select a course to unarchive.");
 1792 
 1793   print CGI::table({class=>"FormLayout"},
 1794     CGI::Tr({},
 1795       CGI::th({class=>"LeftHeader"}, "Course Name:"),
 1796       CGI::td(
 1797         CGI::scrolling_list(
 1798           -name => "unarchive_courseID",
 1799           -values => \@courseIDs,
 1800           -default => $unarchive_courseID,
 1801           -size => 10,
 1802           -multiple => 0,
 1803           -labels => \%courseLabels,
 1804         ),
 1805       ),
 1806     ),
 1807   );
 1808 
 1809   print CGI::p(
 1810     "Currently the unarchive facility is only available for mysql databases.
 1811     It depends on the mysqldump application."
 1812   );
 1813 
 1814 
 1815   print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"unarchive_course", -value=>"Unarchive Course"));
 1816 
 1817   print CGI::end_form();
 1818 }
 1819 
 1820 sub unarchive_course_validate {
 1821   my ($self) = @_;
 1822   my $r = $self->r;
 1823   my $ce = $r->ce;
 1824   #my $db = $r->db;
 1825   #my $authz = $r->authz;
 1826   my $urlpath = $r->urlpath;
 1827 
 1828   my $unarchive_courseID     = $r->param("unarchive_courseID")     || "";
 1829 
 1830   my @errors;
 1831 
 1832   my $new_courseID = $unarchive_courseID; $new_courseID =~ s/\.tar\.gz$//;
 1833 
 1834   if ($new_courseID eq "") {
 1835     push @errors, "You must specify a course name.";
 1836   } elsif ( -d $ce->{webworkDirs}->{courses}."/$new_courseID" ) {
 1837       #Check that a directory for this course doesn't already exist
 1838     push @errors, "A directory already exists with the name $new_courseID.
 1839      You must first delete this existing course before you can unarchive.";
 1840   }
 1841 
 1842 
 1843 
 1844   return @errors;
 1845 }
 1846 
 1847 sub unarchive_course_confirm {
 1848   my ($self) = @_;
 1849   my $r = $self->r;
 1850   my $ce = $r->ce;
 1851   #my $db = $r->db;
 1852   #my $authz = $r->authz;
 1853   #my $urlpath = $r->urlpath;
 1854 
 1855   print CGI::h2("Unarchive Course");
 1856 
 1857   my $unarchive_courseID     = $r->param("unarchive_courseID")     || "";
 1858 
 1859     my $new_courseID = $unarchive_courseID; $new_courseID =~ s/\.tar\.gz$//;
 1860 
 1861 
 1862 
 1863   print CGI::start_form(-method=>"POST", -action=>$r->uri);
 1864     print CGI::p($unarchive_courseID," to course ",
 1865                CGI::input({-name=>'new_courseID', -value=>$new_courseID})
 1866   );
 1867 
 1868   print $self->hidden_authen_fields;
 1869   print $self->hidden_fields("subDisplay");
 1870   print $self->hidden_fields(qw/unarchive_courseID/);
 1871 
 1872   print CGI::p({style=>"text-align: center"},
 1873     CGI::submit(-name=>"decline_unarchive_course", -value=>"Don't unarchive"),
 1874     "&nbsp;",
 1875     CGI::submit(-name=>"confirm_unarchive_course", -value=>"unarchive"),
 1876   );
 1877 
 1878   print CGI::end_form();
 1879 }
 1880 
 1881 sub do_unarchive_course {
 1882   my ($self) = @_;
 1883   my $r = $self->r;
 1884   my $ce = $r->ce;
 1885   #my $db = $r->db;
 1886   #my $authz = $r->authz;
 1887   my $urlpath = $r->urlpath;
 1888   my $new_courseID           = $r->param("new_courseID")           || "";
 1889   my $unarchive_courseID     = $r->param("unarchive_courseID")     || "";
 1890 
 1891   my %dbOptions;
 1892 
 1893   eval {
 1894     unarchiveCourse(
 1895       courseID => $new_courseID,
 1896       archivePath =>$ce->{webworkDirs}->{courses}."/$unarchive_courseID",
 1897       ce => $ce , #   $ce2,
 1898       dbOptions => undef,
 1899     );
 1900   };
 1901 
 1902   if ($@) {
 1903     my $error = $@;
 1904     print CGI::div({class=>"ResultsWithError"},
 1905       CGI::p("An error occured while archiving the course $unarchive_courseID:"),
 1906       CGI::tt(CGI::escapeHTML($error)),
 1907     );
 1908   } else {
 1909     print CGI::div({class=>"ResultsWithoutError"},
 1910       CGI::p("Successfully unarchived  $unarchive_courseID to the course $new_courseID"),
 1911     );
 1912      writeLog($ce, "hosted_courses", join("\t",
 1913         "\tunarchived",
 1914         "",
 1915         "",
 1916         "$unarchive_courseID to $new_courseID",
 1917       ));
 1918 
 1919     my $newCoursePath = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets",
 1920       courseID => $new_courseID);
 1921     my $newCourseURL = $self->systemLink($newCoursePath, authen => 0);
 1922     print CGI::div({style=>"text-align: center"},
 1923       CGI::a({href=>$newCourseURL}, "Log into $new_courseID"),
 1924     );
 1925   }
 1926 }
 1927 
 1928 ################################################################################
 1929 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9