From: jj v. a. <we...@ma...> - 2005-10-02 19:52:03
|
Log Message: ----------- New Config module. It will put its course settings in a file simple.conf along side course.conf. Changes are: URLPath.pm: wiring so the Config module can be called ContentGenerator.pm: link in left panel to Config module CourseEnvironment.pm: evaluate simple.conf. File name can be specified when creating a new CourseEnvironment. This allows it to be bypassed (which is currently used), or to test a simple settings file (not used now, but it might be useful). Constants.pm: all data about configuration is stored here. New values can just be added here, unless they are a new type. Config.pm: new module for limited course configuration. Note, it might be better to move the new variable in Constants.pm to another file. Committing changes to Constants.pm can be an extra hassle since it contains some site specific configuration. Modified Files: -------------- webwork-modperl/lib/WeBWorK: Constants.pm URLPath.pm ContentGenerator.pm CourseEnvironment.pm Added Files: ----------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: Config.pm Revision Data ------------- Index: ContentGenerator.pm =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerato= r.pm,v retrieving revision 1.150 retrieving revision 1.151 diff -Llib/WeBWorK/ContentGenerator.pm -Llib/WeBWorK/ContentGenerator.pm = -u -r1.150 -r1.151 --- lib/WeBWorK/ContentGenerator.pm +++ lib/WeBWorK/ContentGenerator.pm @@ -620,6 +620,7 @@ =09 =09 my $fileMgr =3D $urlpath->newFromModule("${ipfx}FileManager", %args); + my $courseConfig =3D $urlpath->newFromModule("${ipfx}Config", %args); =09 print CGI::hr(); print CGI::start_li(); @@ -686,6 +687,8 @@ if $authz->hasPermissions($user, "send_mail"); print CGI::li(CGI::a({href=3D>$self->systemLink($fileMgr,params=3D>{ = %displayOptions,})}, sp2nbsp($fileMgr->name))) if $authz->hasPermissions($user, "manage_course_files"); + print CGI::li(CGI::a({href=3D>$self->systemLink($courseConfig,params=3D= >{ %displayOptions,})}, sp2nbsp($courseConfig->name))) + if $authz->hasPermissions($user, "manage_course_files"); #print CGI::li(CGI::a({href=3D>$self->systemLink($fileXfer)}, sp2nbsp(= $fileXfer->name))); print CGI::li( $self->helpMacro('instructor_links')); print CGI::end_ul(); Index: CourseEnvironment.pm =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/CourseEnvironme= nt.pm,v retrieving revision 1.27 retrieving revision 1.28 diff -Llib/WeBWorK/CourseEnvironment.pm -Llib/WeBWorK/CourseEnvironment.p= m -u -r1.27 -r1.28 --- lib/WeBWorK/CourseEnvironment.pm +++ lib/WeBWorK/CourseEnvironment.pm @@ -112,17 +112,21 @@ # we need a global environment! $@ and die "Could not evaluate global environment file $globalEnvironme= ntFile: $@"; =09 - # determine location of courseEnvironmentFile + # determine location of courseEnvironmentFile and simple configuration = file # pull it out of $safe's symbol table ad hoc # (we don't want to do the hash conversion yet) no strict 'refs'; my $courseEnvironmentFile =3D ${*{${$safe->root."::"}{courseFiles}}}{en= vironment}; + my $courseWebConfigFile =3D $seedVars{web_config_filename} || + ${*{${$safe->root."::"}{courseFiles}}}{simpleConfig}; use strict 'refs'; =09 # read and evaluate the course environment file # if readFile failed, we don't bother trying to reval my $courseFileContents =3D eval { readFile($courseEnvironmentFile) }; #= catch exceptions $@ or $safe->reval($courseFileContents); + my $courseWebConfigContents =3D eval { readFile($courseWebConfigFile) }= ; # catch exceptions + $@ or $safe->reval($courseWebConfigContents); =09 # get the safe compartment's namespace as a hash no strict 'refs'; Index: URLPath.pm =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/URLPath.pm,v retrieving revision 1.25 retrieving revision 1.26 diff -Llib/WeBWorK/URLPath.pm -Llib/WeBWorK/URLPath.pm -u -r1.25 -r1.26 --- lib/WeBWorK/URLPath.pm +++ lib/WeBWorK/URLPath.pm @@ -72,6 +72,7 @@ instructor_file_transfer /$courseID/instructor/files/ instructor_file_manager /$courseID/instructor/file_manager/ instructor_set_maker /$courseID/instructor/setmaker/ + instructor_config /$courseID/instructor/config/ instructor_compare /$courseID/instructor/compare/ =20 instructor_problem_editor /$courseID/instructor/pgProblemEdit= or/ @@ -237,6 +238,7 @@ kids =3D> [ qw/instructor_user_list instructor_set_list instructor_= add_users instructor_set_assigner instructor_file_transfer instructor_file_mana= ger instructor_problem_editor instructor_set_maker instructor_compare + instructor_config instructor_scoring instructor_scoring_download instructor_mail_merge instructor_answer_log instructor_preflight instructor_statistics instructor_progress =09 @@ -327,6 +329,15 @@ produce =3D> 'assigner/', display =3D> 'WeBWorK::ContentGenerator::Instructor::Assigner', }, + instructor_config =3D> { + name =3D> 'Course Configuration', + parent =3D> 'instructor_tools', + kids =3D> [ qw// ], + match =3D> qr|^config/|, + capture =3D> [ qw// ], + produce =3D> 'config/', + display =3D> 'WeBWorK::ContentGenerator::Instructor::Config', + }, instructor_compare =3D> { name =3D> 'File Compare', parent =3D> 'instructor_tools', Index: Constants.pm =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/Constants.pm,v retrieving revision 1.31 retrieving revision 1.32 diff -Llib/WeBWorK/Constants.pm -Llib/WeBWorK/Constants.pm -u -r1.31 -r1.= 32 --- lib/WeBWorK/Constants.pm +++ lib/WeBWorK/Constants.pm @@ -112,4 +112,106 @@ \end{document} EOF =20 +########################################################################= ######## +# WeBWorK::ContentGenerator::Instructor::Config +########################################################################= ######## + +# Configuation data +# It is organized by section. The allowable types are=20 +# 'text' for a text string (no quote marks allowed), +# 'number' for a number, +# 'list' for a list of text strings, +# 'permission' for a permission value,=20 +# 'boolean' for variables which really hold 0/1 values as flags. +# 'checkboxlist' for variables which really hold a list of values which +# can be independently picked yes/no as checkboxes + +$WeBWorK::ContentGenerator::Instructor::Config::ConfigValues =3D [ + ['General', + { var =3D> 'courseFiles{course_info}', + doc =3D> 'Name of course information file', + doc2 =3D> 'The name of course information file (located in the templ= ates directory). Its contents are displayed in the right panel next to th= e list of homework sets.', + type =3D> 'text'}, + { var =3D> 'sessionKeyTimeout', + doc =3D> 'Inactivity time before a user is required to login again', + doc2 =3D> 'Length of time, in seconds, a user has to be inactive bef= ore he is required to login again.<p> This value should be entered as a n= umber, so as 3600 instead of 60*60 for one hour', + type =3D> 'number'}], + ['Permissions', + { var =3D> 'permissionLevels{login}', + doc =3D> 'Allowed to login to the course', + type =3D> 'permission'}, + { var =3D> 'permissionLevels{change_password}', + doc =3D> 'Allowed to change their password', + doc2 =3D> 'Users at this level and higher are allowed to change thei= r password. Normally guest users are not allowed to change their password= .', + type =3D> 'permission'}, + { var =3D> 'permissionLevels{become_student}', + doc =3D> 'Allowed to <em>act as</em> another user', + type =3D> 'permission'}, + { var =3D> 'permissionLevels{record_answers_when_acting_as_student}', + doc =3D> 'Can submit answers for a student', + doc2 =3D> 'When acting as a student, this permission level and highe= r can submit answers for that student.', + type =3D> 'permission'}, + { var =3D> 'permissionLevels{report_bugs}', + doc =3D> 'Can report bugs', + doc2 =3D> 'Users with at least this permission level get a link in t= he left panel for reporting bugs to the bug tracking system in Rochester'= , + type =3D> 'permission'}, + ], + ['PG - Problem Display/Answer Checking', + { var =3D> 'pg{displayModes}', + doc =3D> 'List of display modes made available to students', + doc2 =3D> 'When viewing a problem, users may choose different method= s of rendering +formulas via an options box in the left panel. Here, you can adjust wha= t display modes are +listed.<p> +Some display modes require other software to be installed on the server.= Be sure to check +that all display modes selected here work from your server.<p> +The display modes are <ul> +<li> plainText: shows the raw LaTeX strings for formulas. +<li> formattedText: formulas are passed through the external program <co= de>tth</code>, +which produces an HTML version of them. Some browsers do not display al= l of the fonts +properly. +<li> images: produces images using the external programs LaTeX and dvipn= g. +<li> jsMath: uses javascript to place symbols, which may come from fonts= or images +(the choice is configurable by the end user). +<li> asciimath: renders formulas client side using ASCIIMathML +</ul> +<p> +You must use at least one display mode. If you select only one, then th= e options box will +not give a choice of modes (since there will only be one active).', + min =3D> 1, + values =3D> ["plainText", "formattedText", "images", "jsMath", "asci= imath"], + type =3D> 'checkboxlist'}, + { var =3D> 'pg{ansEvalDefaults}{useBaseTenLog}', + doc =3D> 'Use log base 10 instead of base <i>e</i>', + doc2 =3D> 'Set to true for log to mean base 10 log and false for log= to mean natural logarithm', + type =3D> 'boolean'}, + { var =3D> 'pg{ansEvalDefaults}{useOldAnswerMacros}', + doc =3D> 'Use older answer checkers', + doc2 =3D> 'During summer 2005, a newer version of the answer checker= s was implemented for answers which are functions and numbers. The newer= checkers allow more functions in student answers, and behave better in c= ertain cases. Some problems are specifically coded to use new (or old) a= nswer checkers. However, for the bulk of the problems, you can choose wh= at the default will be here. <p>Choosing <i>false</i> here means that th= e newer answer checkers will be used by default, and choosing <i>true</i>= means that the old answer checkers will be used by default.', + type =3D> 'boolean'}, + { var =3D> 'pg{ansEvalDefaults}{defaultDisplayMatrixStyle}', + doc =3D> 'Control string for displaying matricies', + doc2 =3D> 'String of three characters for defining the defaults for = displaying matricies. The first and last characters give the left and ri= ght delimiters of the matrix, so usually one of ([| for a left delimiter,= and one of )]| for the right delimiter. It is also legal to specify "."= for no delimiter. <p> The middle character indicates how to display vert= ical lines in a matrix (e.g., for an augmented matrix). This can be s fo= r solid lines and d for dashed lines. While you can specify the defaults= , individual problems may override these values.', + type =3D> 'text'}, + { var =3D> 'pg{ansEvalDefaults}{numRelPercentTolDefault}', + doc =3D> 'Allowed error, as a percentage, for numerical comparisons'= , + doc2 =3D> "When numerical answers are checked, most test if the stud= ent's answer +is close enough to the programmed answer be computing the error as a per= centage of +the correct answer. This value controls the default for how close the s= tudent answer +has to be in order to be marked correct. +<p> +A value such as 0.1 means 0.1 percent error is allowed.", + type =3D> 'number'}, + ], + ['E-Mail', + { var =3D> 'mail{allowedRecipients}', + doc =3D> 'E-mail addresses which can recieve e-mail from a pg proble= m', + doc2 =3D> 'List of e-mail addresses to which e-mail can be sent by a= problem. Professors need to be added to this list if questionaires are u= sed, or other WeBWorK problems which send e-mail as part of their answer = mechanism.', + type =3D> 'list'}, + { var =3D> 'mail{allowedFeedback}', + doc =3D> 'Extra addresses for recieving feedback e-mail', + doc2 =3D> 'By default, feeback is sent to all users who have permiss= ion to receive feedback. If this list is non-empty, feedback is also sent= to the addresses specified here.', + type =3D> 'list'}, + ] +]; + 1; --- /dev/null +++ lib/WeBWorK/ContentGenerator/Instructor/Config.pm @@ -0,0 +1,563 @@ +########################################################################= ######## +# WeBWorK Online Homework Delivery System +# Copyright =A9 2000-2003 The WeBWorK Project, http://openwebwork.sf.net= / +# $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Co= nfig.pm,v 1.1 2005/10/02 19:51:45 jj Exp $ +#=20 +# This program is free software; you can redistribute it and/or modify i= t under +# the terms of either: (a) the GNU General Public License as published b= y the +# Free Software Foundation; either version 2, or (at your option) any la= ter +# version, or (b) the "Artistic License" which comes with this package. +#=20 +# This program is distributed in the hope that it will be useful, but WI= THOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or = FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License o= r the +# Artistic License for more details. +########################################################################= ######## + +# TODO + +# The main package starts lower down. First we define different +# types of config objects. + +# Each config object might want to override the methods display_value, +# entry_widget, and save_string + +########################### config object defaults +package configobject; + +use strict; +use warnings; + +sub new { + my $class =3D shift; + my $self =3D shift; + $self->{Module} =3D shift; + bless $self, $class; + return $self; +} + +# Only input is a value to display, and should produce an html string +sub display_value { + my ($self, $val) =3D @_; + return $val; +} + +# Stringified version for comparison (with html param return value) +sub comparison_value { + my ($self, $val) =3D @_; + return $self->display_value($val); +} + +sub convert_newval_source { + my ($self, $newvalsource) =3D @_; + my $inlinevarname =3D WeBWorK::ContentGenerator::Instructor::Config:= :inline_var($self->{var}); + my $newval; + if($newvalsource =3D~ /widget/) { + $newval =3D $self->{Module}->{r}->param($newvalsource); + } else { + $newval =3D $self->comparison_value(eval('$self->{Module}->{r}->= ce->'. + $inlinevarname)); + } + return($newval); +} + +# Bit of text to put in the configuration file. The result should +# be an assignment which is executable by perl. oldval will be the +# value of the perl variable, and newval will be whatever an entry +# widget produces +sub save_string { + my ($self, $oldval, $newvalsource) =3D @_; + my $varname =3D $self->{var}; + my $newval =3D $self->convert_newval_source($newvalsource); + my $displayoldval =3D $self->display_value($oldval); + return '' if($displayoldval eq $newval); + return('$'. $varname . " =3D '$newval';\n"); +} + +# A widget to interact with the user +sub entry_widget { + my ($self, $name, $default) =3D @_; + return CGI::textfield( + -name =3D> $name, + -value =3D> $default, + -size =3D> '15', + ); +} + +# This produces the documentation string and image link to more +# documentation. It is the same for all config types. +sub what_string { + my $self =3D shift; + my $r =3D $self->{Module}->r; + return(CGI::td( + CGI::a({href=3D>$self->{Module}->systemLink( + $r->urlpath->new(type=3D>'instructor_config', + args=3D>{courseID =3D> $r->urlpath->arg("courseID")}), + params=3D>{show_long_doc=3D>$self->{doc2} || $self->{doc},=20 + var_name=3D>"$self->{var}"}), + target=3D>"_blank"}, + CGI::img({src=3D>$r->{ce}->{webworkURLs}->{htdocs}. + "/images/question_mark.png", + border=3D>"0", alt=3D>"$self->{var}",=20 + style=3D>"float: right; padding-left: 0.1em;"}) + ) . + $self->{doc}=20 + )); +} + +########################### configtext +package configtext; +@configtext::ISA =3D qw(configobject); + +sub save_string { + my ($self, $oldval, $newvalsource) =3D @_; + my $varname =3D $self->{var}; + my $newval =3D $self->convert_newval_source($newvalsource); + my $displayoldval =3D $self->comparison_value($oldval); + return '' if($displayoldval eq $newval); + # Remove quotes from the string, we will have a new type for text with = quotes + $newval =3D~ s/['"`]//g; + return('$'. $varname . " =3D '$newval';\n"); +} + +########################### confignumber +package confignumber; +@confignumber::ISA =3D qw(configobject); + +sub save_string { + my ($self, $oldval, $newvalsource) =3D @_; + my $varname =3D $self->{var}; + my $newval =3D $self->convert_newval_source($newvalsource); + my $displayoldval =3D $self->comparison_value($oldval); + # Remove quotes from the string, we will have a new type for text with = quotes + $newval =3D~ s/['"`]//g; + my $newval2 =3D eval($newval); + if($@) { + $self->{Module}->addbadmessage("Syntax error in numeric value '$newval= ' for variable \$$self->{var}. Reverting to the system default value."); + return ''; + } + return '' if($displayoldval =3D=3D $newval2); + return('$'. $varname . " =3D $newval;\n"); +} + +########################### configboolean +package configboolean; +@configboolean::ISA =3D qw(configobject); + +sub display_value { + my ($self, $val) =3D @_; + return 'True' if $val; + return 'False'; +} + +sub save_string { + my ($self, $oldval, $newvalsource) =3D @_; + my $varname =3D $self->{var}; + my $newval =3D $self->convert_newval_source($newvalsource); + my $displayoldval =3D $self->comparison_value($oldval); + return '' if($displayoldval eq $newval); + return('$'. $varname . " =3D " . ($newval eq 'True' ? 1 : 0) .";\n"); +} + +sub entry_widget { + my ($self, $name, $default) =3D @_; +=09 + return CGI::popup_menu( + -name =3D> $name, + -default =3D> ($default ? 'True' : 'False'), + -values =3D> ['True', 'False'], + ); +} + + +########################### configpermission +package configpermission; +@configpermission::ISA =3D qw(configobject); + +# This tries to produce a string from a permission number. If you feed = it +# a string, that's what you get back. +sub display_value { + my ($self, $val) =3D @_; + return 'nobody' if not defined($val); + my %userRoles =3D %{$self->{Module}->{r}->{ce}->{userRoles}}; + my %reverseUserRoles =3D reverse %userRoles; + return $reverseUserRoles{$val} if defined($reverseUserRoles{$val}); + return $val; +} + +sub save_string { + my ($self, $oldval, $newvalsource) =3D @_; + my $varname =3D $self->{var}; + my $newval =3D $self->convert_newval_source($newvalsource); + my $displayoldval =3D $self->comparison_value($oldval); + return '' if($displayoldval eq $newval); + my $str =3D '$'. $varname . " =3D '$newval';\n"; + $str =3D '$'. $varname . " =3D undef;\n" if $newval eq 'nobody'; + return($str); +} + +sub entry_widget { + my ($self, $name, $default) =3D @_; + my $ce =3D $self->{Module}->{r}->{ce}; + my $permHash =3D {}; + my %userRoles =3D %{$ce->{userRoles}}; + $userRoles{nobody} =3D 99999999; # insure that nobody comes at the end + my %reverseUserRoles =3D reverse %userRoles; + + # the value of a permission can be undefined (for nobody), + # a standard permission number, or some other number + if(not defined($default)) { + $default =3D 'nobody'; + } + + my @values =3D sort { $userRoles{$a} <=3D> $userRoles{$b} } keys %userR= oles; + return CGI::popup_menu(-name=3D>$name, -values =3D> \@values, + -default=3D>$default); +} + +########################### configlist +package configlist; +@configlist::ISA =3D qw(configobject); + +sub display_value { + my ($self, $val) =3D @_; + return ' ' if not defined($val); + my $str =3D join(',<br>', @{$val}); + $str =3D ' ' if $str !~ /\S/; + return $str; +} + +sub comparison_value { + my ($self, $val) =3D @_; + $val =3D [] if not defined($val); + my $str =3D join(',', @{$val}); + return($str); +} + +sub save_string { + my ($self, $oldval, $newvalsource) =3D @_; + my $newval =3D $self->convert_newval_source($newvalsource); + my $varname =3D $self->{var}; + $oldval =3D $self->comparison_value($oldval); + return '' if($oldval eq $newval); + my $str =3D ''; + + $oldval =3D~ s/^\s*(.*)\s*$/$1/; + $newval =3D~ s/^\s*(.*)\s*$/$1/; + $oldval =3D~ s/[\s,]+/,/sg; + $newval =3D~ s/[\s,]+/,/sg; + return '' if($newval eq $oldval); + # ok we really have a new value, now turn it back into a string + my @parts =3D split ',', $newval; + map { $_ =3D~ s/['"`]//g } @parts; + @parts =3D map { "'". $_ ."'" } @parts; + $str =3D join(',', @parts); + $str =3D '$'. $varname . " =3D [$str];\n"; + return($str); +} + +sub entry_widget { + my ($self, $name, $default) =3D @_; + + $default =3D [] if not defined($default); + my $str =3D join(', ', @{$default}); + $str =3D '' if $str !~ /\S/; + return CGI::textarea( + -name =3D> $name, + -rows =3D> 4, + -value =3D> $str, + -columns =3D> 25, + ); +} + +########################### configcheckboxlist +package configcheckboxlist; +@configcheckboxlist::ISA =3D qw(configobject); + +sub display_value { + my ($self, $val) =3D @_; + $val =3D [] if not defined($val); + my @vals =3D @$val; + return join(',<br>', @vals); +} + +# here r->param() returns an array, so we need a custom +# version of convert_newval_source + +sub convert_newval_source { + my ($self, $newvalsource) =3D @_; + my $inlinevarname =3D WeBWorK::ContentGenerator::Instructor::Config:= :inline_var($self->{var}); + my @newvals; + if($newvalsource =3D~ /widget/) { + @newvals =3D $self->{Module}->{r}->param($newvalsource); + } else { + my $newval =3D eval('$self->{Module}->{r}->{ce}->'. $inlinevarna= me); + @newvals =3D @$newval; + } + return(@newvals); +} + +# Bit of text to put in the configuration file. The result should +sub save_string { + my ($self, $oldval, $newvalsource) =3D @_; + my $varname =3D $self->{var}; + my @newvals =3D $self->convert_newval_source($newvalsource); + if(scalar(@newvals) =3D=3D 0) { + $self->{Module}->addbadmessage("You need to select at least one displa= y mode."); + if($newvalsource =3D~ /widget/) { + return $self->save_string($oldval, 'current'); # try to return the ol= d saved value + } else { + return '' ; # the previous saved value was empty, reset to system def= ault + } + } + $oldval =3D $self->comparison_value($oldval); + my $newval =3D $self->comparison_value(\@newvals); + return '' if($oldval eq $newval); + @newvals =3D map { "'".$_."'" } @newvals; + my $str =3D join(',', @newvals); + $str =3D '$'. $varname . " =3D [$str];\n"; + return($str); +} + +sub comparison_value { + my ($self, $val) =3D @_; + $val =3D [] if not defined($val); + my $str =3D join(',', @{$val}); + return($str); +} + +sub entry_widget { + my ($self, $name, $default) =3D @_; + return CGI::checkbox_group( + -name =3D> $name, + -value =3D> $self->{values}, + -default =3D> $default, + -columns=3D>1 + ); +} + +########### Main Config Package starts here + +package WeBWorK::ContentGenerator::Instructor::Config; +use base qw(WeBWorK::ContentGenerator::Instructor); + +=3Dhead1 NAME + +WeBWorK::ContentGenerator::Instructor::Config - Config + +=3Dcut + +use strict; +use warnings; + +use CGI::Pretty qw(); +use WeBWorK::CourseEnvironment; + +# Load the configuration parts defined in Constants.pm=20 + +our $ConfigValues =3D [] unless defined $ConfigValues; + +# Configuation data +# It is organized by section. The allowable types are=20 +# 'text' for a text string, +# 'list' for a list of text strings, +# 'permission' for a permission value,=20 +# 'boolean' for variables which really hold 0/1 values as flags. + +# write contents to outputFilePath and return error messages if any +sub writeFile { + my $outputFilePath =3D shift; + my $contents =3D shift; + my $writeFileErrors; + eval { =20 + local *OUTPUTFILE; + if( open OUTPUTFILE, ">", $outputFilePath) { + print OUTPUTFILE $contents; + close OUTPUTFILE; + } else { + $writeFileErrors =3D "I could not open $outputFilePath". + CGI::br() . CGI::br(). + "We will not be able to make configuration changes unless the permis= sions are set so that the web server can write to this file."; + } + }; # any errors are caught in the next block + + $writeFileErrors =3D $@ if $@; + return($writeFileErrors); +} + +# Make a new config object from data + +sub objectify { + my ($self, $data) =3D @_; + return "config$data->{type}"->new($data,$self); +} + + +# Take var string from ConfigValues and prepare it for $ce->... +sub inline_var { + my $varstring =3D shift; + return '{'.$varstring.'}' if $varstring =3D~ /^\w+$/; + $varstring =3D~ s/^(\w+)/{$1}->/; + return($varstring); +} + +sub print_navigation_tabs { + my ($self, $current_tab, @tab_names) =3D @_; + my $r =3D $self->r; + my $str =3D ''; + for my $tab (0..(scalar(@tab_names)-1)) { + if($current_tab eq "tab$tab") { + $tab_names[$tab] =3D $tab_names[$tab]; + } else { + $tab_names[$tab] =3D CGI::a({href=3D>$self->systemLink($r->urlpath, p= arams=3D>{section_tab=3D>"tab$tab"})}, $tab_names[$tab]); + } + } + print CGI::p() . + '<div align=3D"center">' . join(' | ', @tab_names) .'</div>'. + CGI::p(); +} + +sub pre_header_initialize { + my ($self) =3D @_; + my $r =3D $self->r; + my $ce =3D $r->ce; + + # Get a course environment without course.conf + $self->{default_ce} =3D WeBWorK::CourseEnvironment->new( + { webwork_dir =3D> $ce->{webworkDirs}->{root} }); + + $self->{ce_file_dir} =3D $ce->{courseDirs}->{root}; + + # Get a copy of the course environment which does not have simple.conf = loaded + my $ce3 =3D eval { WeBWorK::CourseEnvironment->new( + { webwork_dir =3D> $ce->{webworkDirs}->{root},=20 + courseName =3D> $ce->{courseName}, + web_config_filename =3D> 'noSuchFilePlease', + }) }; + if($r->param("make_changes")) { + my $widget_count =3D 0; + my $fileoutput =3D "#!perl +# This file is automatically generated by WeBWorK's web-based +# configuration module. Do not make changes directly to this +# file. It will be overwritten the next time configuration +# changes are saved.\n\n"; + + # Get the number of the current tab + my $tab =3D $r->param('section_tab') || 'tab0'; + $tab =3D~ s/tab//; + # We completely rewrite the simple configuration file + # so we need to go through all sections + for my $configSection (@{$ConfigValues}) { + my @configSectionArray =3D @{$configSection}; + shift @configSectionArray; + for my $con (@configSectionArray) { + my $conobject =3D $self->objectify($con); + if($tab) { # This tab is not being shown + my $oldval =3D eval('$ce3->'.inline_var($con->{var})); + $fileoutput .=3D $conobject->save_string($oldval, 'current'); + } else { # We reached the tab with entry objects + $fileoutput .=3D $conobject->save_string(eval('$ce3->'.inline_var($= con->{var})), "widget$widget_count"); + $widget_count++; + } + } + $tab--; + } + my $write_result =3D writeFile($self->{ce_file_dir}."/simple.conf", $f= ileoutput); + if ($write_result) { + $self->addbadmessage($write_result); + } else { + $self->addgoodmessage("Changes saved."); + } + } +} + +sub body { + my ($self) =3D @_; + + my $r =3D $self->r; + my $ce =3D $r->ce; # course environment + my $db =3D $r->db; # database + + my $userName =3D $r->param('user'); + + my $user =3D $db->getUser($userName); # checked + die "record for user $userName (real user) does not exist." + unless defined $user; + + ### Check that this is a professor + my $authz =3D $r->authz; + unless ($authz->hasPermissions($userName, "modify_problem_sets")) { + print "User $userName returned " . + $authz->hasPermissions($user, "modify_problem_sets") . + " for permission"; + return(CGI::div({class=3D>'ResultsWithError'}, + CGI::em("You are not authorized to access the Instructor tools."))); + } + + if ($r->param('show_long_doc')) { + print CGI::h2("Variable Documentation: ". CGI::code('$'.$r->param('var= _name'))), + CGI::p(), + CGI::blockquote( $r->param('show_long_doc')); + return ""; + } + + my $default_ce =3D $self->{default_ce}; + # Get the current course environment again in case we just saved change= s + my $ce4 =3D eval { WeBWorK::CourseEnvironment->new( + { webwork_dir =3D> $ce->{webworkDirs}->{root},=20 + webwork_url =3D> $ce->{webwork_url}, + pg_dir =3D> $ce->{pg_dir}, + courseName =3D> $ce->{courseName}, + }) }; + + my $widget_count =3D 0; + if(scalar(@$ConfigValues) =3D=3D 0) { + print CGI::p("The configuration module did not find the data +it needs to function. Have your site administrator check that Constants= .pm +is up to date."); + return ""; + } + + # Start tabs at the top + my $current_tab =3D $r->param('section_tab') || 'tab0'; + my @tab_names =3D map { $_->[0] } @{$ConfigValues}; + $self->print_navigation_tabs($current_tab, @tab_names); + + print CGI::startform({method=3D>"post", action=3D>$r->uri, name=3D>"con= figform"}); + print $self->hidden_authen_fields(); + print CGI::hidden(-name=3D> 'section_tab', -value=3D> $current_tab); + + my $tabnumber =3D $current_tab; + $tabnumber =3D~ s/tab//; + my @configSectionArray =3D @{$ConfigValues->[$tabnumber]}; + my $configTitle =3D shift @configSectionArray; + print CGI::p(CGI::div({-align=3D>'center'}, CGI::b($configTitle))); + + print '<table border=3D"1">'; + print '<tr>'.CGI::th('What'). CGI::th('Default') .CGI::th('Current'); + for my $con (@configSectionArray) { + my $conobject =3D $self->objectify($con); + print "\n<tr>"; + print $conobject->what_string; + print CGI::td($conobject->display_value(eval('$default_ce->'.inline_va= r($con->{var})))); + print CGI::td($conobject->entry_widget("widget$widget_count", eval('$c= e4->'.inline_var($con->{var})))); + $widget_count++; + } + print "</table>"; + print CGI::p(CGI::submit(-name=3D>'make_changes', -value=3D>'Save Chang= es')); + print CGI::end_form(); + + + return "";=09 +} + + +=3Dhead1 AUTHOR + +Written by John Jones, jj (at) asu.edu. + +=3Dcut + + + +1; |