[system] / branches / rel-2-2-dev / webwork-modperl / lib / WeBWorK / CourseEnvironment.pm Repository:
ViewVC logotype

View of /branches/rel-2-2-dev/webwork-modperl/lib/WeBWorK/CourseEnvironment.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3879 - (download) (as text) (annotate)
Sat Jan 7 02:08:53 2006 UTC (7 years, 4 months ago)
File size: 11319 byte(s)
This commit was manufactured by cvs2svn to create branch 'rel-2-2-dev'.

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork2/lib/WeBWorK/CourseEnvironment.pm,v 1.29 2005/10/05 18:16:51 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::CourseEnvironment;
   18 
   19 =head1 NAME
   20 
   21 WeBWorK::CourseEnvironment - Read configuration information from global.conf
   22 and course.conf files.
   23 
   24 =head1 SYNOPSIS
   25 
   26  use WeBWorK::CourseEnvironment;
   27  $ce = WeBWorK::CourseEnvironment->new({
   28   webwork_url         => "/webwork2",
   29   webwork_dir         => "/opt/webwork2",
   30   pg_dir              => "/opt/pg",
   31   webwork_htdocs_url  => "/webwork2_files",
   32   webwork_htdocs_dir  => "/opt/webwork2/htdocs",
   33   webwork_courses_url => "/webwork2_course_files",
   34   webwork_courses_dir => "/opt/webwork2/courses",
   35   courseName          => "name_of_course",
   36  });
   37 
   38  my $timeout = $courseEnv->{sessionKeyTimeout};
   39  my $mode    = $courseEnv->{pg}->{options}->{displayMode};
   40  # etc...
   41 
   42 =head1 DESCRIPTION
   43 
   44 The WeBWorK::CourseEnvironment module reads the system-wide F<global.conf> and
   45 course-specific F<course.conf> files used by WeBWorK to calculate and store
   46 settings needed throughout the system. The F<.conf> files are perl source files
   47 that can contain any code allowed under the default safe compartment opset.
   48 After evaluation of both files, any package variables are copied out of the
   49 safe compartment into a hash. This hash becomes the course environment.
   50 
   51 =cut
   52 
   53 use strict;
   54 use warnings;
   55 use Carp;
   56 use Safe;
   57 use WeBWorK::Utils qw(readFile);
   58 use WeBWorK::Debug;
   59 use Opcode qw(empty_opset);
   60 
   61 =head1 CONSTRUCTION
   62 
   63 =over
   64 
   65 =item new(HASHREF)
   66 
   67 HASHREF is a reference to a hash containing scalar variables with which to seed
   68 the course environment. It must contain at least a value for the key
   69 C<webworkRoot>.
   70 
   71 The C<new> method finds the file F<conf/global.conf> relative to the given
   72 C<webwork_dir> directory. After reading this file, it uses the
   73 C<$courseFiles{environment}> variable, if present, to locate the course
   74 environment file. If found, the file is read and added to the environment.
   75 
   76 =item new(ROOT URLROOT PGROOT COURSENAME)
   77 
   78 A deprecated form of the constructor in which four seed variables are given
   79 explicitly: C<webwork_dir>, C<webwork_url>, C<pg_dir>, and C<courseName>.
   80 
   81 =cut
   82 
   83 # NEW SYNTAX
   84 #
   85 # new($invocant, $seedVarsRef)
   86 #   $invocant       implicitly set by caller
   87 #   $seedVarsRef    reference to hash containing scalar variables with which to
   88 #                   seed the course environment
   89 #
   90 # OLD SYNTAX
   91 #
   92 # new($invocant, $webworkRoot, $webworkURLRoot, $pgRoot, $courseName)
   93 #   $invocant          implicitly set by caller
   94 #   $webworkRoot       directory that contains the WeBWorK distribution
   95 #   $webworkURLRoot    URL that points to the WeBWorK system
   96 #   $pgRoot            directory that contains the PG distribution
   97 #   $courseName        name of the course being used
   98 sub new {
   99   my ($invocant, @rest) = @_;
  100   my $class = ref($invocant) || $invocant;
  101 
  102   # contains scalar symbols/values with which to seed course environment
  103   my %seedVars;
  104 
  105   # where do we get the seed variables?
  106   if (ref $rest[0] eq "HASH") {
  107     %seedVars = %{$rest[0]};
  108   } else {
  109     debug __PACKAGE__, ": deprecated four-argument form of new() used.\n";
  110     #$seedVars{webworkRoot}    = $rest[0];
  111     #$seedVars{webworkURLRoot} = $rest[1];
  112     #$seedVars{pgRoot}         = $rest[2];
  113     $seedVars{webwork_dir}    = $rest[0];
  114     $seedVars{webwork_url}    = $rest[1];
  115     $seedVars{pg_dir}         = $rest[2];
  116     $seedVars{courseName}     = $rest[3];
  117   }
  118 
  119   my $safe = Safe->new;
  120 
  121   # seed course environment with initial values
  122   while (my ($var, $val) = each %seedVars) {
  123     $val = "" if not defined $val;
  124     $safe->reval("\$$var = '$val';");
  125   }
  126 
  127   # Compile the "include" function with all opcodes available.
  128   my $include = q[ sub include {
  129     my ($file) = @_;
  130     my $fullPath = "].$seedVars{webwork_dir}.q[/$file";
  131     # This regex matches any string that begins with "../",
  132     # ends with "/..", contains "/../", or is "..".
  133     if ($fullPath =~ m!(?:^|/)\.\.(?:/|$)!) {
  134       die "Included file $file has potentially insecure path: contains \"..\"";
  135     } else {
  136       local @INC = ();
  137       my $result = do $fullPath;
  138       if ($!) {
  139         warn "Failed to read include file $fullPath (has it been created from the corresponding .dist file?): $!";
  140       } elsif ($@) {
  141         warn "Failed to compile include file $fullPath: $@";
  142       } elsif (not $result) {
  143         warn "Include file $fullPath did not return a true value.";
  144       }
  145     }
  146   } ];
  147 
  148   my $maskBackup = $safe->mask;
  149   $safe->mask(empty_opset);
  150   $safe->reval($include);
  151   $@ and die "Failed to reval include subroutine: $@";
  152   $safe->mask($maskBackup);
  153 
  154   # determine location of globalEnvironmentFile
  155   my $globalEnvironmentFile = "$seedVars{webwork_dir}/conf/global.conf";
  156 
  157   # read and evaluate the global environment file
  158   my $globalFileContents = readFile($globalEnvironmentFile);
  159   $safe->reval($globalFileContents);
  160 
  161   # if that evaluation failed, we can't really go on...
  162   # we need a global environment!
  163   $@ and die "Could not evaluate global environment file $globalEnvironmentFile: $@";
  164 
  165   # determine location of courseEnvironmentFile and simple configuration file
  166   # pull it out of $safe's symbol table ad hoc
  167   # (we don't want to do the hash conversion yet)
  168   no strict 'refs';
  169   my $courseEnvironmentFile = ${*{${$safe->root."::"}{courseFiles}}}{environment};
  170   my $courseWebConfigFile = $seedVars{web_config_filename} ||
  171     ${*{${$safe->root."::"}{courseFiles}}}{simpleConfig};
  172   use strict 'refs';
  173 
  174   # read and evaluate the course environment file
  175   # if readFile failed, we don't bother trying to reval
  176   my $courseFileContents = eval { readFile($courseEnvironmentFile) }; # catch exceptions
  177   $@ or $safe->reval($courseFileContents);
  178   my $courseWebConfigContents = eval { readFile($courseWebConfigFile) }; # catch exceptions
  179   $@ or $safe->reval($courseWebConfigContents);
  180 
  181   # get the safe compartment's namespace as a hash
  182   no strict 'refs';
  183   my %symbolHash = %{$safe->root."::"};
  184   use strict 'refs';
  185 
  186   # convert the symbol hash into a hash of regular variables.
  187   my $self = {};
  188   foreach my $name (keys %symbolHash) {
  189     # weed out internal symbols
  190     next if $name =~ /^(INC|_|__ANON__|main::)$/;
  191     # pull scalar, array, and hash values for this symbol
  192     my $scalar = ${*{$symbolHash{$name}}};
  193     my @array = @{*{$symbolHash{$name}}};
  194     my %hash = %{*{$symbolHash{$name}}};
  195     # for multiple variables sharing a symbol, scalar takes precedence
  196     # over array, which takes precedence over hash.
  197     if (defined $scalar) {
  198       $self->{$name} = $scalar;
  199     } elsif (@array) {
  200       $self->{$name} = \@array;
  201     } elsif (%hash) {
  202       $self->{$name} = \%hash;
  203     }
  204   }
  205 
  206   bless $self, $class;
  207 
  208   # here is where we can do evil things to the course environment *sigh*
  209   # anything changed has to be done here. after this, CE is considered read-only
  210   # anything added must be prefixed with an underscore.
  211 
  212   # create reverse-lookup hash mapping status abbreviations to real names
  213   $self->{_status_abbrev_to_name} = {
  214     map { my $name = $_; map { $_ => $name } @{$self->{statuses}{$name}{abbrevs}} }
  215       keys %{$self->{statuses}}
  216   };
  217 
  218   # now that we're done, we can go ahead and return...
  219   return $self;
  220 }
  221 
  222 =back
  223 
  224 =head1 ACCESS
  225 
  226 There are no formal accessor methods. However, since the course environemnt is
  227 a hash of hashes and arrays, is exists as the self hash of an instance
  228 variable:
  229 
  230   $ce->{someKey}{someOtherKey};
  231 
  232 =head1 EXPERIMENTAL ACCESS METHODS
  233 
  234 This is an experiment in extending CourseEnvironment to know a little more about
  235 its contents, and perform useful operations for me.
  236 
  237 There is a set of operations that require certain data from the course
  238 environment. Most of these are un Utils.pm. I've been forced to pass $ce into
  239 them, so that they can get their data out. But some things are so intrinsically
  240 linked to the course environment that they might as well be methods in this
  241 class.
  242 
  243 =head2 STATUS METHODS
  244 
  245 =over
  246 
  247 =item status_abbrev_to_name($status_abbrev)
  248 
  249 Given the abbreviation for a status, return the name. Returns undef if the
  250 abbreviation is not found.
  251 
  252 =cut
  253 
  254 sub status_abbrev_to_name {
  255   my ($ce, $status_abbrev) = @_;
  256   if (not defined $status_abbrev or $status_abbrev eq "") {
  257     carp "status_abbrev_to_name: status_abbrev (first argument) must be defined and non-empty";
  258     return;
  259   }
  260 
  261   return $ce->{_status_abbrev_to_name}{$status_abbrev};
  262 }
  263 
  264 =item status_name_to_abbrevs($status_name)
  265 
  266 Returns the list of abbreviations for a given status. Returns an empty list if
  267 the status is not found.
  268 
  269 =cut
  270 
  271 sub status_name_to_abbrevs {
  272   my ($ce, $status_name) = @_;
  273   if (not defined $status_name or $status_name eq "") {
  274     carp "status_name_to_abbrevs: status_name (first argument) must be defined and non-empty";
  275     return;
  276   }
  277 
  278   return unless exists $ce->{statuses}{$status_name};
  279   return @{$ce->{statuses}{$status_name}{abbrevs}};
  280 }
  281 
  282 =item status_has_behavior($status_name, $behavior)
  283 
  284 Return true if $status_name lists $behavior.
  285 
  286 =cut
  287 
  288 sub status_has_behavior {
  289   my ($ce, $status_name, $behavior) = @_;
  290   if (not defined $status_name or $status_name eq "") {
  291     carp "status_has_behavior: status_name (first argument) must be defined and non-empty";
  292     return;
  293   }
  294   if (not defined $behavior or $behavior eq "") {
  295     carp "status_has_behavior: behavior (second argument) must be defined and non-empty";
  296     return;
  297   }
  298 
  299   if (exists $ce->{statuses}{$status_name}) {
  300     if (exists $ce->{statuses}{$status_name}{behaviors}) {
  301       my $num_matches = grep { $_ eq $behavior } @{$ce->{statuses}{$status_name}{behaviors}};
  302       return $num_matches > 0;
  303     } else {
  304       return 0; # no behaviors
  305     }
  306   } else {
  307     warn "status '$status_name' not found in \%statuses -- assuming no behaviors.\n";
  308     return 0;
  309   }
  310 }
  311 
  312 =item status_abbrev_has_behavior($status_abbrev, $behavior)
  313 
  314 Return true if the status abbreviated by $status_abbrev lists $behavior.
  315 
  316 =cut
  317 
  318 sub status_abbrev_has_behavior {
  319   my ($ce, $status_abbrev, $behavior) = @_;
  320   if (not defined $status_abbrev or $status_abbrev eq "") {
  321     carp "status_abbrev_has_behavior: status_abbrev (first argument) must be defined and non-empty";
  322     return;
  323   }
  324   if (not defined $behavior or $behavior eq "") {
  325     carp "status_abbrev_has_behavior: behavior (second argument) must be defined and non-empty";
  326     return;
  327   }
  328 
  329   my $status_name = $ce->status_abbrev_to_name($status_abbrev);
  330   if (defined $status_name) {
  331     return $ce->status_has_behavior($status_name, $behavior);
  332   } else {
  333     warn "status abbreviation '$status_abbrev' not found in \%statuses -- assuming no behaviors.\n";
  334   }
  335 }
  336 
  337 =back
  338 
  339 =cut
  340 
  341 1;
  342 
  343 # perl doesn't look like line noise. line noise has way more alphanumerics.

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9