[system] / trunk / webwork2 / lib / WeBWorK / ContentGenerator / CourseAdmin.pm Repository:
ViewVC logotype

View of /trunk/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4357 - (download) (as text) (annotate)
Tue Aug 8 16:03:25 2006 UTC (6 years, 10 months ago) by sh002i
File size: 57503 byte(s)
removed vestigal gdbm/sql options. fix how dblayout radio buttons are
generated, so that the default layout or selected is selected correctly.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9