[system] / branches / rel-2-2-dev / webwork2 / lib / WeBWorK / ContentGenerator / Instructor / ProblemSetEditor.pm Repository:
ViewVC logotype

View of /branches/rel-2-2-dev/webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2103 - (download) (as text) (annotate)
Thu May 13 20:42:35 2004 UTC (9 years ago) by toenail
Original Path: trunk/webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm
File size: 16892 byte(s)
added pop up list of set header files
fixed false successful save message

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm,v 1.50 2004/05/13 16:02:55 toenail 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::Instructor::ProblemSetEditor;
   18 use base qw(WeBWorK::ContentGenerator::Instructor);
   19 
   20 =head1 NAME
   21 
   22 WeBWorK::ContentGenerator::Instructor::ProblemSetEditor - Edit a set definition list
   23 
   24 =cut
   25 
   26 use strict;
   27 use warnings;
   28 use CGI qw();
   29 use File::Copy;
   30 use WeBWorK::DB::Record::Problem;
   31 use WeBWorK::Utils qw(readFile formatDateTime parseDateTime list2hash readDirectory max);
   32 
   33 our $rowheight = 20;  #controls the length of the popup menus.
   34 our $libraryName;  #library directory name
   35 
   36 use constant SET_FIELDS => [qw(open_date due_date answer_date set_header problem_header published)];
   37 use constant PROBLEM_FIELDS =>[qw(source_file value max_attempts)];
   38 use constant PROBLEM_USER_FIELDS => [qw(problem_seed status num_correct num_incorrect)];
   39 
   40 sub getSetName {
   41   my ($self, $pathSetName) = @_;
   42   if (ref $pathSetName eq "HASH") {
   43     $pathSetName = undef;
   44   }
   45   return $pathSetName;
   46 }
   47 
   48 # One wrinkle here: if $override is undefined, do the global thing,
   49 # otherwise, it's truth value determines the checkbox and the current fieldValue is not directly editable
   50 sub setRowHTML {
   51   my ($description, $fieldName, $fieldValue, $size, $override, $overrideValue) = @_;
   52 
   53   my $attributeHash = {type=>"text", name=>$fieldName, value=>$fieldValue};
   54   $attributeHash->{size} = $size if defined $size;
   55 
   56   my $input = (defined $override) ? $fieldValue : CGI::input($attributeHash);
   57 
   58   my $html = CGI::td({}, [$description, $input]);
   59 
   60   if (defined $override) {
   61     $attributeHash->{name}="${fieldName}_override";
   62     $attributeHash->{value}=($override ? $overrideValue : "" );
   63 
   64     $html .= CGI::td({}, [
   65       CGI::checkbox({
   66         type=>"checkbox",
   67         name=>"override",
   68         label=>"override with:",
   69         value=>$fieldName,
   70         checked=>($override ? 1 : 0)
   71       }),
   72       CGI::input($attributeHash)
   73     ]);
   74   }
   75 
   76   return $html;
   77 
   78 }
   79 
   80 
   81 # FIXME: this or something similar could get pulled up to Instructor.pm
   82 sub recurseDirectory {
   83 
   84   my ($self, $dir, $pattern) = @_;
   85 
   86   my @dirs = grep {$_ ne "." and $_ ne ".." and $_ ne "Library" and $_ ne "CVS" and -d "$dir/$_"} readDirectory($dir);
   87 
   88   my @files = map { "$dir/$_" } $self->read_dir($dir, $pattern);
   89 
   90   foreach (@dirs) {
   91     push (@files, $self->recurseDirectory("$dir/$_", $pattern));
   92   }
   93 
   94   return @files;
   95 }
   96 
   97 
   98 
   99 # Initialize does all of the form processing.  It's extensive, and could probably be cleaned up and
  100 # consolidated with a little abstraction.
  101 sub initialize {
  102   my ($self)      = @_;
  103   my $r           = $self->r;
  104   my $db          = $r->db;
  105   my $ce          = $r->ce;
  106   my $authz       = $r->authz;
  107   my $user        = $r->param('user');
  108   #my $setName    = $self->getSetName(@components);
  109   my $setName     = $r->urlpath->arg("setID");
  110   my $setRecord   = $db->getGlobalSet($setName); #checked
  111   die "global set $setName not found." unless $setRecord;
  112 
  113   $self->{set}    = $setRecord;
  114   my @editForUser = $r->param('editForUser');
  115   # some useful booleans
  116   my $forUsers    = scalar(@editForUser);
  117   my $forOneUser  = $forUsers == 1;
  118 
  119   # build a quick lookup table
  120   my %overrides = list2hash $r->param('override');
  121 
  122   unless ($authz->hasPermissions($user, "modify_problem_sets")) {
  123     $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify problem sets")));
  124     return;
  125   }
  126 
  127   ###################################################
  128   # The set form was submitted with the save button pressed
  129   # Save changes to the set
  130   ###################################################
  131 
  132   if (defined($r->param('submit_set_changes'))) {
  133 
  134     if (!$forUsers) {
  135       foreach (@{SET_FIELDS()}) {
  136         if (defined($r->param($_))) {
  137           if (m/_date$/) {
  138             $setRecord->$_(parseDateTime($r->param($_)));
  139           } else {
  140             $setRecord->$_($r->param($_));
  141             if($_ eq 'set_header') {
  142             # be nice and copy the default file here if it doesn't exist yet
  143             # empty set headers lead to trouble
  144               my $newheaderpath = $r->{ce}->{courseDirs}->{templates} . '/'. $r->param('set_header');
  145               unless(($r->param('set_header') !~ /\S/) or -e $newheaderpath) {
  146                 my $default_header = $ce->{webworkFiles}->{screenSnippets}->{setHeader};
  147                 File::Copy::copy($default_header, $newheaderpath);
  148               }
  149             }
  150           }
  151         } else {
  152           if (m/published$/) {
  153             $setRecord->$_(0);
  154           }
  155         }
  156       }
  157 
  158 
  159 
  160 
  161       ###################################################
  162       # Check that the open, due and answer dates are in increasing order.
  163       # Bail if this is not correct.
  164       ###################################################
  165       if ($setRecord->open_date > $setRecord->due_date)  {
  166         $self->addmessage(CGI::div({class=>'ResultsWithError'},'Error: Due date must come after open date'));
  167         return;
  168       }
  169       if ($setRecord->due_date > $setRecord->answer_date) {
  170         $self->addmessage(CGI::div({class=>'ResultsWithError'},'Error: Answer date must come after due date'));
  171         return;
  172       }
  173       ###################################################
  174       # End date check section.
  175       ###################################################
  176       $self->addmessage(CGI::div({class=>'ResultsWithoutError'}, "Changes to set $setName were successfully saved."));
  177       $db->putGlobalSet($setRecord);
  178     } else {
  179 
  180       my $userSetRecord = $db->getUserSet($editForUser[0], $setName); #checked
  181       die "set $setName not found for $editForUser[0]." unless $userSetRecord;
  182       foreach my $field (@{SET_FIELDS()}) {
  183         if (defined $r->param("${field}_override")) {
  184           if (exists $overrides{$field}) {
  185             if ($field =~ m/_date$/) {
  186               $userSetRecord->$field(parseDateTime($r->param("${field}_override")));
  187             } else {
  188               $userSetRecord->$field($r->param("${field}_override"));
  189             }
  190           } else {
  191             $userSetRecord->$field(undef);
  192           }
  193         }
  194       }
  195       ###################################################
  196       # Check that the open, due and answer dates are in increasing order.
  197       # Bail if this is not correct.
  198       ###################################################
  199       my $active_open_date   = $userSetRecord->open_date   ? $userSetRecord->open_date   : $setRecord->open_date;
  200       my $active_due_date    = $userSetRecord->due_date    ? $userSetRecord->due_date    : $setRecord->due_date;
  201       my $active_answer_date = $userSetRecord->answer_date ? $userSetRecord->answer_date : $setRecord->answer_date;
  202       if ( $active_open_date > $active_due_date ) {
  203         $self->addmessage(CGI::div({class=>'ResultsWithError'},'Error: Due date override must come after open date'));
  204         return;
  205       }
  206       if ( $active_due_date > $active_answer_date ) {
  207         $self->addmessage(CGI::div({class=>'ResultsWithError'},'Error: Answer date override must come after due date'));
  208         return;
  209       }
  210       ###################################################
  211       # End date check section.
  212       ###################################################
  213       $self->addmessage(CGI::div({class=>'ResultsWithoutError'}, "Changes to set $setName for user ",CGI::b($editForUser[0]) ,"were successfully saved."));
  214       $db->putUserSet($userSetRecord);
  215     }
  216 
  217   }
  218 
  219   ###################################################
  220   # The set form was submitted with the export button pressed
  221   # Export the set structure to a set definition file
  222   ###################################################
  223 
  224   if (  defined($r->param('export_set'))  ) {
  225     my $fileName = $r->param('export_file_name');
  226     die "Please specify a file name for saving the set definition" unless $fileName;
  227     $fileName    .= '.def' unless $fileName =~ /\.def$/;
  228     my $filePath  = $ce->{courseDirs}->{templates}.'/'.$fileName;
  229     # back up existing file
  230     if(-e $filePath) {
  231         rename($filePath,"$filePath.bak") or
  232                die "Can't rename $filePath to $filePath.bak ",
  233                    "Check permissions for webserver on directories. $!";
  234     }
  235       my $openDate     = formatDateTime($setRecord->open_date);
  236       my $dueDate      = formatDateTime($setRecord->due_date);
  237       my $answerDate   = formatDateTime($setRecord->answer_date);
  238       my $setHeader    = $setRecord->set_header;
  239 
  240       my @problemList = $db->listGlobalProblems($setName);
  241       my $problemList  = '';
  242       foreach my $prob (sort {$a <=> $b} @problemList) {
  243         my $problemRecord = $db->getGlobalProblem($setName, $prob); # checked
  244         die "global problem $prob for set $setName not found" unless defined($problemRecord);
  245         my $source_file   = $problemRecord->source_file();
  246       my $value         = $problemRecord->value();
  247       my $max_attempts  = $problemRecord->max_attempts();
  248         $problemList     .= "$source_file, $value, $max_attempts \n";
  249       }
  250       my $fileContents = <<EOF;
  251 
  252 openDate          = $openDate
  253 dueDate           = $dueDate
  254 answerDate        = $answerDate
  255 paperHeaderFile   = $setHeader
  256 screenHeaderFile  = $setHeader
  257 problemList       =
  258 
  259 $problemList
  260 
  261 
  262 
  263 EOF
  264 
  265 
  266       $self->saveProblem($fileContents, $filePath);
  267       $self->addmessage(CGI::div({class=>"ResultsWithoutError"}, CGI::p("Set definition saved to $filePath")));
  268 
  269   }
  270 }
  271 
  272 
  273 sub body {
  274   my ($self, @components) = @_;
  275   my $r                   = $self->r;
  276   my $urlpath             = $r->urlpath;
  277   my $db                  = $r->db;
  278   my $ce                  = $r->ce;
  279   my $authz               = $r->authz;
  280   my $user                = $r->param('user');
  281   my $courseName          = $urlpath->arg("courseID");
  282   my $setName             = $urlpath->arg("setID");
  283   my $setRecord           = $db->getGlobalSet($setName);  # checked
  284   die "global set $setName not found." unless $setRecord;
  285   my @editForUser         = $r->param('editForUser');
  286   # some useful booleans
  287   my $forUsers            = scalar(@editForUser);
  288   my $forOneUser          = $forUsers == 1;
  289 
  290         return CGI::em("You are not authorized to access the Instructor tools.") unless $authz->hasPermissions($user, "access_instructor_tools");
  291 
  292   ## Set Form ##
  293   my $userSetRecord;
  294   my %overrideArgs;
  295   if ($forOneUser) {
  296     $userSetRecord = $db->getUserSet($editForUser[0], $setName); #checked
  297     die "set $setName not found for user $editForUser[0]." unless $userSetRecord;
  298     foreach my $field (@{SET_FIELDS()}) {
  299       $overrideArgs{$field} = [defined $userSetRecord->$field, ($field =~ /_date$/ ? formatDateTime($userSetRecord->$field) : $userSetRecord->$field)];
  300     }
  301   } else {
  302     foreach my $field (@{SET_FIELDS()}) {
  303       $overrideArgs{$field} = [undef, undef];
  304     }
  305   }
  306   print CGI::h2({}, "Set Data"), "\n";
  307   if (@editForUser) {
  308     print CGI::p("Editing user-specific overrides for ". CGI::b(join ", ", @editForUser));
  309   }
  310 
  311   my @headers = $self->recurseDirectory($self->{ce}->{courseDirs}->{templates}, '(?i)header.*?\\.pg$');
  312   map { s|^$self->{ce}->{courseDirs}->{templates}/?|| } @headers;
  313   @headers = sort @headers;
  314 
  315   print CGI::start_form({method=>"post", action=>$r->uri}), "\n";
  316   print CGI::table({},
  317     CGI::Tr({}, [
  318       setRowHTML( "Open Date:",
  319             "open_date",
  320             formatDateTime($setRecord->open_date),
  321             undef,
  322             @{$overrideArgs{open_date}})."\n",
  323       setRowHTML( "Due Date:",
  324             "due_date",
  325             formatDateTime($setRecord->due_date),
  326             undef,
  327             @{$overrideArgs{due_date}})."\n",
  328       setRowHTML( "Answer Date:",
  329             "answer_date",
  330             formatDateTime($setRecord->answer_date),
  331             undef,
  332             @{$overrideArgs{answer_date}})."\n",
  333 #     setRowHTML( "Set Header:", "set_header",
  334 #           $setRecord->set_header,
  335 #           32,
  336 #           @{$overrideArgs{set_header}})."\n",
  337 # FIXME  we're not using this right at the moment as far as I know.  There may someday be a use for it, so don't take this out yet.
  338 #       setRowHTML( "Problem Header:",
  339 #             "problem_header",
  340 #             $setRecord->problem_header,
  341 #             undef,
  342 #             @{$overrideArgs{problem_header}})."\n",
  343       CGI::td({}, ["Set Header:" , ($forOneUser) ? $setRecord->set_header
  344                   : CGI::popup_menu(-name=>'set_header',
  345                         -values=>\@headers,
  346                         -default=>$setRecord->set_header)]) . "\n",
  347     ])
  348   );
  349 
  350   if (@editForUser) {
  351     my $publishedColor = ($setRecord->published) ? "Published" : "Unpublished";
  352     print CGI::p("This set is currently", CGI::font({class=>$publishedColor}, (($setRecord->published) ? "Published" : "Unpublished")), CGI::br(), "(You cannot publish/unpublish a set for specific users.)");
  353   } else {
  354     print CGI::checkbox({type=>"checkbox", name=>"published", label=>"Published", value=>"1", checked=>(($setRecord->published) ? 1 : 0)}), CGI::br();
  355 
  356   }
  357 
  358   print $self->hiddenEditForUserFields(@editForUser),
  359         $self->hidden_authen_fields,
  360         CGI::input({type=>"submit", name=>"submit_set_changes", value=>"Save Set", style=>"{width: 13ex}"}),
  361         '&nbsp;';
  362 
  363     #### link to edit setHeader
  364     my $PGProblemEditor    = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
  365                                                      courseID  => $courseName,
  366                                                      setID     => $setName,
  367                                                      problemID => '0'
  368     );
  369     my $setHeaderEditLink = $self->systemLink($PGProblemEditor);
  370   if (defined($setRecord) and $setRecord->set_header) {
  371     print CGI::a({-href=>$setHeaderEditLink},'Edit set header: '.$setRecord->set_header);
  372   }
  373 
  374   print CGI::br(),
  375         CGI::submit({ name=>"export_set", label=>"Export Set",  style=>"{width: 13ex}"} ),
  376         ' as ',
  377         CGI::input({type=>'text',name=>'export_file_name',value=>"set$setName.def",size=>32});
  378 
  379   print CGI::br();
  380 
  381 
  382 
  383   print CGI::end_form();
  384 
  385   my $problemCount = $db->listGlobalProblems($setName);
  386   print CGI::h2({}, "Problems"), "\n";
  387   print CGI::p({}, "This set contains $problemCount problem" . ($problemCount == 1 ? "" : "s").".");
  388   #FIXME
  389   # the code below doesn't work ---
  390   # get message
  391   #no type matches module WeBWorK::ContentGenerator::Instructor::SetsAssignedToUser with args at
  392   # /home/gage/webwork/webwork-modperl/lib/WeBWorK/URLPath.pm line 497.
  393     # error in URLPath.pm??????
  394   my $problemSetListPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ProblemList",
  395                                                     courseID => $courseName,
  396                                                     setID    => $setName
  397   );
  398 
  399   my $editProblemsURL        = $self->systemLink($problemSetListPage,
  400                                                  params => ['editForUser']   # include all editForUser parameters
  401   );
  402   my $usersAssignedToSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet",
  403                                                     courseID => $courseName,
  404                                                     setID    => $setName
  405   );
  406 
  407   my $editUsersAssignedToSetURL        = $self->systemLink($usersAssignedToSetPage,
  408 
  409   );
  410   print CGI::a({href=>$editProblemsURL},
  411    (@editForUser) ? "Edit the list of problems in this set for ". CGI::b(join ", ", @editForUser) :
  412                     "Edit the list of problems in this set");
  413 
  414   unless (@editForUser) {      # this is not needed when we are editing details for a user
  415     my $userCount = $db->listUsers;
  416     my $usersOfSet = $db->countSetUsers($setName);
  417     print CGI::h2({}, "Users"), "\n";
  418     print CGI::p({}, "This set is assigned to ".$self->userCountMessage($usersOfSet, $userCount).".");
  419     print CGI::a({href=>$editUsersAssignedToSetURL}, "Determine who this set is assigned to");
  420   }
  421 
  422   return "";
  423 }
  424 ###########################################################################
  425 # utility
  426 ###########################################################################
  427 sub saveProblem {
  428     my $self      = shift;
  429   my ($body, $probFileName)= @_;
  430   local(*PROBLEM);
  431   open (PROBLEM, ">$probFileName") ||
  432     $self->submission_error("Could not open $probFileName for writing.
  433     Check that the  permissions for this problem are 660 (-rw-rw----)");
  434   print PROBLEM $body;
  435   close PROBLEM;
  436   chmod 0660, "$probFileName" ||
  437                $self->submission_error("
  438                       CAN'T CHANGE PERMISSIONS ON FILE $probFileName");
  439 }
  440 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9