You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(58) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(53) |
Feb
(56) |
Mar
|
Apr
|
May
(30) |
Jun
(78) |
Jul
(121) |
Aug
(155) |
Sep
(77) |
Oct
(61) |
Nov
(45) |
Dec
(94) |
2006 |
Jan
(116) |
Feb
(33) |
Mar
(11) |
Apr
(23) |
May
(60) |
Jun
(89) |
Jul
(130) |
Aug
(109) |
Sep
(124) |
Oct
(63) |
Nov
(82) |
Dec
(45) |
2007 |
Jan
(31) |
Feb
(35) |
Mar
(123) |
Apr
(36) |
May
(18) |
Jun
(134) |
Jul
(133) |
Aug
(241) |
Sep
(126) |
Oct
(31) |
Nov
(15) |
Dec
(5) |
2008 |
Jan
(11) |
Feb
(6) |
Mar
(16) |
Apr
(29) |
May
(43) |
Jun
(149) |
Jul
(27) |
Aug
(29) |
Sep
(37) |
Oct
(20) |
Nov
(4) |
Dec
(6) |
2009 |
Jan
(34) |
Feb
(30) |
Mar
(16) |
Apr
(6) |
May
(1) |
Jun
(32) |
Jul
(22) |
Aug
(7) |
Sep
(18) |
Oct
(50) |
Nov
(22) |
Dec
(8) |
2010 |
Jan
(17) |
Feb
(15) |
Mar
(10) |
Apr
(9) |
May
(67) |
Jun
(30) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
|
From: dpvc v. a. <we...@ma...> - 2005-10-02 23:22:22
|
Log Message: ----------- Removed unneeded "my" for a varaible that is already local. Modified Files: -------------- pg/macros: parserImplicitPlane.pl Revision Data ------------- Index: parserImplicitPlane.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserImplicitPlane.pl,v retrieving revision 1.7 retrieving revision 1.8 diff -Lmacros/parserImplicitPlane.pl -Lmacros/parserImplicitPlane.pl -u -r1.7 -r1.8 --- macros/parserImplicitPlane.pl +++ macros/parserImplicitPlane.pl @@ -140,7 +140,7 @@ return $f if Value::isFormula($f); my $isEquals = ref($f) eq 'ImplicitPlane::equality'; $f = bless $f, 'Parser::BOP::equality' if $isEquals; # so Parser will recognize it - my $f = Value::Formula->create($f,@_); + $f = Value::Formula->create($f,@_); $f = $self->new($f) if $isEquals || ref($f->{tree}) eq 'ImplicitPlane::equality'; return $f; } |
From: dpvc v. a. <we...@ma...> - 2005-10-02 22:55:08
|
Log Message: ----------- Handle the names of entries in Lists and Formulas returning lists better. Also do better typechecking on these lists. Finally, allow better typechecking for String objects that are being used when a Formula answer is allowed. Modified Files: -------------- pg/lib/Value: AnswerChecker.pm Revision Data ------------- Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.66 retrieving revision 1.67 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.66 -r1.67 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -610,7 +610,7 @@ sub typeMatch { my $self = shift; my $other = shift; my $ans = shift; - return 0 if ref($other) && Value::isFormula($other); +# return 0 if ref($other) && Value::isFormula($other); my $typeMatch = $ans->{typeMatch}; return 1 if !Value::isValue($typeMatch) || $typeMatch->class eq 'String' || $self->type eq $other->type; @@ -986,7 +986,7 @@ entry_type => undef, list_type => undef, typeMatch => $element, - extra => $element, + extra => undef, requireParenMatch => 1, removeParens => 1, ); @@ -1028,7 +1028,7 @@ my $ltype = $ans->{list_type} || lc($self->type); my $stype = $ans->{short_type} || $ltype; - $value = (Value::isValue($typeMatch)? lc($typeMatch->cmp_class): 'value') + $value = (Value::isValue($typeMatch)? lc($typeMatch->cmp_class): 'a value') unless defined($value); $value =~ s/(real|complex) //; $ans->{cmp_class} = $value; $value =~ s/^an? //; $value = 'formula' if $value =~ m/formula/; @@ -1137,7 +1137,7 @@ my $ordered = $ans->{ordered}; my $showTypeWarnings = $ans->{showTypeWarnings} && !$ans->{isPreview}; my $typeMatch = $ans->{typeMatch}; - my $extra = $ans->{extra}; + my $extra = $ans->{extra} || $typeMatch; my $showHints = getOption($ans,'showHints') && !$ans->{isPreview}; my $error = $$Value::context->{error}; my $score = 0; my @errors; my $i = 0; @@ -1159,7 +1159,7 @@ # Some words differ if ther eis only one entry in the student's list # my $nth = ''; my $answer = 'answer'; - my $class = $ans->{list_type} || $self->cmp_class; + my $class = $ans->{list_type} || $ans->{cmp_class}; if ($m > 1) { $nth = ' '.$self->NameForNumber($i); $class = $ans->{cmp_class}; @@ -1267,10 +1267,13 @@ showDomainErrors => 1, ) if defined(%$type) && $self->type ne 'List'; + my $element; + if ($self->{tree}->class eq 'List') {$element = Value::Formula->new($self->{tree}{coords}[0])} + else {$element = Value::Formula->new(($self->createRandomPoints(1))[1]->[0]{data}[0])} return ( Value::List::cmp_defaults($self,@_), removeParens => $self->{autoFormula}, - typeMatch => Value::Formula->new(($self->createRandomPoints(1))[1]->[0]{data}[0]), + typeMatch => $element, showDomainErrors => 1, ); } |
From: jj v. a. <we...@ma...> - 2005-10-02 20:40:50
|
Log Message: ----------- Added some todo items, and fixed minor bug with the minimum number of displayModes (it was supposed to be a variable). Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: Config.pm Revision Data ------------- Index: Config.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Config.pm,v retrieving revision 1.1 retrieving revision 1.2 diff -Llib/WeBWorK/ContentGenerator/Instructor/Config.pm -Llib/WeBWorK/ContentGenerator/Instructor/Config.pm -u -r1.1 -r1.2 --- lib/WeBWorK/ContentGenerator/Instructor/Config.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Config.pm @@ -15,6 +15,12 @@ ################################################################################ # TODO +# convert more html to CGI:: calls +# put some formatting in css and in ur.css +# add type to deal with boxes around problem text +# maybe add a type to deal with files like ur.css and templates (where +# a copy of the old file gets created for the course and then the +# user can modify it). # The main package starts lower down. First we define different # types of config objects. @@ -302,8 +308,8 @@ my ($self, $oldval, $newvalsource) = @_; my $varname = $self->{var}; my @newvals = $self->convert_newval_source($newvalsource); - if(scalar(@newvals) == 0) { - $self->{Module}->addbadmessage("You need to select at least one display mode."); + if($self->{min} and (scalar(@newvals) < $self->{min})) { + $self->{Module}->addbadmessage("You need to select at least $self->{min} display mode."); if($newvalsource =~ /widget/) { return $self->save_string($oldval, 'current'); # try to return the old saved value } else { |
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; |
From: jj v. a. <we...@ma...> - 2005-10-02 19:45:05
|
Log Message: ----------- Add location/name of simple configuration file. It is written by the Config module. Modified Files: -------------- webwork-modperl/conf: global.conf.dist Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/conf/global.conf.dist,v retrieving revision 1.144 retrieving revision 1.145 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.144 -r1.145 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -319,6 +319,9 @@ # The course configuration file. $courseFiles{environment} = "$courseDirs{root}/course.conf"; +# The course simple configuration file (holds web-based configuratoin). +$courseFiles{simpleConfig} = "$courseDirs{root}/simple.conf"; + # File contents are displayed after login, on the problem sets page. Path given # here is relative to the templates directory. $courseFiles{course_info} = "course_info.txt"; |
From: dpvc v. a. <we...@ma...> - 2005-10-02 19:31:30
|
Log Message: ----------- Correctly create Formula returning List when a list has formula entries. Modified Files: -------------- pg/lib/Value: List.pm Revision Data ------------- Index: List.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/List.pm,v retrieving revision 1.17 retrieving revision 1.18 diff -Llib/Value/List.pm -Llib/Value/List.pm -u -r1.17 -r1.18 --- lib/Value/List.pm +++ lib/Value/List.pm @@ -30,8 +30,8 @@ $p = [$p,@_] if (ref($p) ne 'ARRAY' || scalar(@_) > 0); my $type; foreach my $x (@{$p}) { - $isFormula = 1 if Value::isFormula($x); $x = Value::makeValue($x) unless ref($x); + $isFormula = 1 if Value::isFormula($x); if (Value::isValue($x)) { if (!$type) {$type = $x->type} else {$type = 'unknown' unless $type eq $x->type} |
From: dpvc v. a. <we...@ma...> - 2005-10-02 19:13:54
|
Log Message: ----------- Major update to allow ImplicitPlane objects to be created automatically by the Parser when it sees equal signs. This makes it possible to use ImplicitPlanes in Lists. (Before, when the List was turned into a Formula returning a List, the equality became a plane old equality rather than an ImplicitPlane object.) This is a bit of a hack for now until I can make List and other Value objects be allowed to have Formulas as their entries rather than turn into Formulas returning Lists (or whatever type). Modified Files: -------------- pg/macros: parserImplicitPlane.pl Revision Data ------------- Index: parserImplicitPlane.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserImplicitPlane.pl,v retrieving revision 1.6 retrieving revision 1.7 diff -Lmacros/parserImplicitPlane.pl -Lmacros/parserImplicitPlane.pl -u -r1.6 -r1.7 --- macros/parserImplicitPlane.pl +++ macros/parserImplicitPlane.pl @@ -51,11 +51,13 @@ # $context{ImplicitPlane} = Context("Vector")->copy(); $context{ImplicitPlane}->{precedence}{ImplicitPlane} = Context()->{precedence}{special}; +$context{ImplicitPlane}->{value}{Formula} = "ImplicitPlane"; Context("ImplicitPlane"); # # allow equalities in formulas # Parser::BOP::equality::Allow; +$context{ImplicitPlane}->operators->set('=' => {class => 'ImplicitPlane::equality'}); # # Syntactic sugar for creating implicit planes @@ -89,7 +91,7 @@ $type = 'line' if scalar(@{$vars}) == 2; my @terms = (); my $i = 0; foreach my $x (@{$vars}) {push @terms, $N->{data}[$i++]->string.$x} - $plane = Value::Formula->new(join(' + ',@terms).' = '.$d->string)->reduce(@_); + $plane = Value::Formula->create(join(' + ',@terms).' = '.$d->string)->reduce(@_); } else { # # Determine the normal vector and d value from the equation @@ -119,6 +121,7 @@ Value::Error("Your formula isn't a linear one") unless (Value::Formula->new($plane->{tree}{lop}) - Value::Formula->new($plane->{tree}{rop})) == $f; + $plane = $plane->reduce; } Value::Error("The equation of a %s must be non-zero somewhere",$type) if ($N->norm == 0); @@ -128,12 +131,30 @@ } # -# We already know the vectors are none zero, so check +# Substitute for Context()->{value}{Formula} which creates +# an implicit plane if there is an equality, otherwise +# creates a regular formula. +# +sub create { + my $self = shift; my $f = shift; + return $f if Value::isFormula($f); + my $isEquals = ref($f) eq 'ImplicitPlane::equality'; + $f = bless $f, 'Parser::BOP::equality' if $isEquals; # so Parser will recognize it + my $f = Value::Formula->create($f,@_); + $f = $self->new($f) if $isEquals || ref($f->{tree}) eq 'ImplicitPlane::equality'; + return $f; +} + +# +# We already know the vectors are non-zero, so check # if the equations are multiples of each other. +# (If the comparison is to a string, mark it wrong, otherwise +# turn the right-hand side into an implicit plane) # sub compare { my ($l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->compare($l,!$flag)} + return 1 if Value::isValue($r) && $r->type eq 'String'; $r = ImplicitPlane->new($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} my ($lN,$ld) = ($l->{N},$l->{d}); @@ -158,7 +179,22 @@ # sub typeMatch { my $self = shift; my $other = shift; my $ans = shift; + return ref($other) && $other->type eq 'Equality' unless ref($self); return ref($other) && $self->type eq $other->type; } +# +# We subclass BOP::equality so that we can give a warning about +# things like 1 = 3 +# +package ImplicitPlane::equality; +our @ISA = qw(Parser::BOP::equality); + +sub _check { + my $self = shift; + $self->SUPER::_check; + $self->Error("An implicit equation can't be constant on both sides") + if $self->{lop}{isConstant} && $self->{rop}{isConstant}; +} + 1; |
From: Sam H. v. a. <we...@ma...> - 2005-10-02 18:09:31
|
Log Message: ----------- fix error reporting for include() function. Modified Files: -------------- webwork2/lib/WeBWorK: CourseEnvironment.pm Revision Data ------------- Index: CourseEnvironment.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/CourseEnvironment.pm,v retrieving revision 1.26 retrieving revision 1.27 diff -Llib/WeBWorK/CourseEnvironment.pm -Llib/WeBWorK/CourseEnvironment.pm -u -r1.26 -r1.27 --- lib/WeBWorK/CourseEnvironment.pm +++ lib/WeBWorK/CourseEnvironment.pm @@ -84,15 +84,13 @@ die "Included file $file has potentially insecure path: contains \"..\""; } else { local @INC = (); - unless (my $result = do $fullPath) { - # FIXME: "do" is misbehaving: if there's a syntax error, $@ - # should be set to the error string, but it's not getting set. - # $! is set to an odd error message "Broken pipe" or something. - # On the command line, both $! and $@ are set in the case of a - # syntax error. This just means that errors will be confusing. - $! and die "Failed to read include file $fullPath: $! (has it been created from the corresponding .dist file?)"; - $@ and die "Failed to compile include file $fullPath: $@"; - die "Include file $fullPath did not return a true value."; + my $result = do $fullPath; + if ($!) { + warn "Failed to read include file $fullPath (has it been created from the corresponding .dist file?): $!"; + } elsif ($@) { + warn "Failed to compile include file $fullPath: $@"; + } elsif (not $result) { + warn "Include file $fullPath did not return a true value."; } } } ]; @@ -102,7 +100,7 @@ $safe->reval($include); $@ and die "Failed to reval include subroutine: $@"; $safe->mask($maskBackup); - + # determine location of globalEnvironmentFile my $globalEnvironmentFile = "$seedVars{webwork_dir}/conf/global.conf"; |
From: Sam H. v. a. <we...@ma...> - 2005-10-02 18:09:22
|
Log Message: ----------- give user an error message if they can't act as another user. see bug #846. Modified Files: -------------- webwork2/lib: WeBWorK.pm Revision Data ------------- Index: WeBWorK.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK.pm,v retrieving revision 1.75 retrieving revision 1.76 diff -Llib/WeBWorK.pm -Llib/WeBWorK.pm -u -r1.75 -r1.76 --- lib/WeBWorK.pm +++ lib/WeBWorK.pm @@ -234,44 +234,41 @@ debug("Now we deal with the effective user:\n"); my $eUserID = $r->param("effectiveUser") || $userID; debug("userID=$userID eUserID=$eUserID\n"); - # FIXME: hasPermissions does nothing with $eUserID, and lately we want it to - # only accept two arguments, so we're removing $eUserID from this call. - #my $su_authorized = $authz->hasPermissions($userID, "become_student", $eUserID); - my $su_authorized = $authz->hasPermissions($userID, "become_student"); - if ($su_authorized) { - debug("Ok, looks like you're allowed to become $eUserID. Whoopie!\n"); - } else { - debug("Uh oh, you're not allowed to become $eUserID. Nice try!\n"); - $eUserID = $userID; + if ($userID ne $eUserID) { + debug("userID and eUserID differ... seeing if userID has 'become_student' permission.\n"); + my $su_authorized = $authz->hasPermissions($userID, "become_student"); + if ($su_authorized) { + debug("Ok, looks like you're allowed to become $eUserID. Whoopie!\n"); + } else { + debug("Uh oh, you're not allowed to become $eUserID. Nice try!\n"); + $eUserID = $userID; + $r->notes("authen_error" => "You do not have permission to become another user."); + $displayModule = AUTHEN_MODULE; + } } + + # set effectiveUser in case it was changed or not set to begin with $r->param("effectiveUser" => $eUserID); - # if we're doing a proctored test, after the user has been authenticated - # we need to also check on the proctor. note that in the gateway quiz - # module we double check this, to be sure that someone isn't taking a - # proctored quiz but calling the unproctored ContentGenerator + + # if we're doing a proctored test, after the user has been authenticated + # we need to also check on the proctor. note that in the gateway quiz + # module we double check this, to be sure that someone isn't taking a + # proctored quiz but calling the unproctored ContentGenerator my $urlProducedPath = $urlPath->path(); - if ( $urlProducedPath =~ /proctored_quiz_mode/i ) { my $procAuthOK = $authen->verifyProctor(); - if ( $procAuthOK ) { - my $proctorUserID = $r->param("proctor_user"); - my $proctor_authorized = - $authz->hasPermissions($proctorUserID, - "proctor_quiz", $userID); - if ( ! $proctor_authorized ) { - $r->notes("authen_error", - "Proctor $proctorUserID is not " . - "authorized to proctor tests in " . - "this course."); - $displayModule = PROCTOR_AUTHEN_MODULE; + if ($procAuthOK) { + my $proctorUserID = $r->param("proctor_user"); + my $proctor_authorized = $authz->hasPermissions($proctorUserID, "proctor_quiz"); + unless ($proctor_authorized) { + $r->notes("authen_error", "User $proctorUserID is not authorized to proctor tests in this course."); + $displayModule = PROCTOR_AUTHEN_MODULE; + } + } else { + $displayModule = PROCTOR_AUTHEN_MODULE; } - - } else { - $displayModule = PROCTOR_AUTHEN_MODULE; - } } - } else { debug("Bad news: authentication failed!\n"); $displayModule = AUTHEN_MODULE; |
From: Sam H. <sh...@ma...> - 2005-10-01 04:02:06
|
On Sep 30, 2005, at 15:16, Sam Hathaway via activitymail wrote: > Log Message: > ----------- > modifications to support changes to global.conf.dist adding %userRoles > hash and changing the meaning of values in %permissionLevels. > > Authz will now look up the role obtained from %permissionLevels in the > %userRoles hash, and compare the resulting number with the user's > permission level. The other half of this log message was supposed to be: > implemented named roles for permissions system. %userRoles maps role > names to numeric permission levels, and %permissionLevels now maps > activities* to role names. A slight change to Authz.pm accompanies > this > change to do the role-to-permission-level lookups. > > * a.k.a. operations, privilegespermissions, gotta get the terminology > straight. think of the users! > > %userRoles should be used by modules such as UserList for providing > more > intelligent editing of the permission_level field. Another possible > use > would be to for pretty-printing user types -- a user could be called a > student, ta, professor, etc. based on what role corresponded to their > permission_level. |
From: Sam H. v. a. <we...@ma...> - 2005-09-30 20:45:42
|
Log Message: ----------- modifications to support changes to global.conf.dist adding %userRoles hash and changing the meaning of values in %permissionLevels. Authz will now look up the role obtained from %permissionLevels in the %userRoles hash, and compare the resulting number with the user's permission level. Modified Files: -------------- webwork2/conf: global.conf.dist webwork2/lib/WeBWorK: Authz.pm Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/global.conf.dist,v retrieving revision 1.142 retrieving revision 1.143 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.142 -r1.143 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -476,81 +476,89 @@ # Authorization system ################################################################################ -# This lets you specify a minimum permission level needed to perform certain -# actions. For each pair in the hash below, in order to perform the action -# described by the key, the user must have a permission level greater than or -# equal to the value. - -my $guest = -5; -my $student = 0; -my $proctor = 2; -my $ta = 5; -my $professor = 10; -my $nobody = undef; +# this section lets you define which groups of users can perform which actions. +# this hash maps a numeric permission level to the name of a role. the number +# assigned to a role is significant -- roles with higher numbers are considered +# "more privileged", and are included when that role is listed for a privilege +# below. +# +%userRoles = ( + guest => -5, + student => 0, + proctor => 2, + ta => 5, + professor => 10, +); + +# this hash maps operations to the roles that are allowed to perform those +# operations. the role listed and any role with a higher permission level (in +# the %userRoles hash) will be allowed to perform the operation. If the role +# is undefined, no users will be allowed to perform the operation. +# %permissionLevels = ( - login => $guest, - report_bugs => $student, - submit_feedback => $student, - change_password => $student, - change_email_address => $student, + login => "guest", + report_bugs => "student", + submit_feedback => "student", + change_password => "student", + change_email_address => "student", - proctor_quiz => $proctor, + proctor_quiz => "proctor", - view_multiple_sets => $ta, - view_unopened_sets => $ta, - view_unpublished_sets => $ta, - view_answers => $ta, + view_multiple_sets => "ta", + view_unopened_sets => "ta", + view_unpublished_sets => "ta", + view_answers => "ta", - become_student => $professor, - access_instructor_tools => $ta, - score_sets => $professor, - send_mail => $professor, - receive_feedback => $ta, + become_student => "professor", + access_instructor_tools => "ta", + score_sets => "professor", + send_mail => "professor", + receive_feedback => "ta", - create_and_delete_problem_sets => $professor, - assign_problem_sets => $professor, - modify_problem_sets => $professor, - modify_student_data => $professor, - modify_classlist_files => $professor, - modify_set_def_files => $professor, - modify_scoring_files => $professor, - modify_problem_template_files => $professor, + create_and_delete_problem_sets => "professor", + assign_problem_sets => "professor", + modify_problem_sets => "professor", + modify_student_data => "professor", + modify_classlist_files => "professor", + modify_set_def_files => "professor", + modify_scoring_files => "professor", + modify_problem_template_files => "professor", - create_and_delete_courses => $professor, - fix_course_databases => $professor, + create_and_delete_courses => "professor", + fix_course_databases => "professor", ##### Behavior of the interactive problem processor ##### - show_correct_answers_before_answer_date => $ta, - show_solutions_before_answer_date => $ta, - avoid_recording_answers => $ta, + show_correct_answers_before_answer_date => "ta", + show_solutions_before_answer_date => "ta", + avoid_recording_answers => "ta", # Below this level, old answers are never initially shown - can_show_old_answers_by_default => $student, + can_show_old_answers_by_default => "student", # at this level, we look at showOldAnswers for default value # even after the due date - can_always_use_show_old_answers_default => $professor, - check_answers_before_open_date => $ta, - check_answers_after_open_date_with_attempts => $ta, - check_answers_after_open_date_without_attempts => $guest, - check_answers_after_due_date => $guest, - check_answers_after_answer_date => $guest, - record_answers_when_acting_as_student => $nobody, + can_always_use_show_old_answers_default => "professor", + check_answers_before_open_date => "ta", + check_answers_after_open_date_with_attempts => "ta", + check_answers_after_open_date_without_attempts => "guest", + check_answers_after_due_date => "guest", + check_answers_after_answer_date => "guest", + record_answers_when_acting_as_student => undef, # "record_answers_when_acting_as_student" takes precedence # over the following for professors acting as students: - record_answers_before_open_date => $nobody, - record_answers_after_open_date_with_attempts => $student, - record_answers_after_open_date_without_attempts => $nobody, - record_answers_after_due_date => $nobody, - record_answers_after_answer_date => $nobody, - dont_log_past_answers => $professor, + record_answers_before_open_date => undef, + record_answers_after_open_date_with_attempts => "student", + record_answers_after_open_date_without_attempts => undef, + record_answers_after_due_date => undef, + record_answers_after_answer_date => undef, + dont_log_past_answers => "professor", ##### Behavior of the Hardcopy Processor ##### - download_hardcopy_multiuser => $ta, - download_hardcopy_multiset => $ta, - download_hardcopy_format_pdf => $guest, - download_hardcopy_format_tex => $ta, + download_hardcopy_multiuser => "ta", + download_hardcopy_multiset => "ta", + download_hardcopy_format_pdf => "guest", + download_hardcopy_format_tex => "ta", ); ################################################################################ Index: Authz.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Authz.pm,v retrieving revision 1.22 retrieving revision 1.23 diff -Llib/WeBWorK/Authz.pm -Llib/WeBWorK/Authz.pm -u -r1.22 -r1.23 --- lib/WeBWorK/Authz.pm +++ lib/WeBWorK/Authz.pm @@ -59,6 +59,7 @@ use strict; use warnings; +use Carp qw/croak/; ################################################################################ @@ -146,7 +147,7 @@ if (@_ != 3) { shift @_; # get rid of self my $nargs = @_; - die "hasPermissions called with $nargs arguments instead of the expected 2: '@_'" + croak "hasPermissions called with $nargs arguments instead of the expected 2: '@_'" } my ($self, $userID, $activity) = @_; @@ -162,7 +163,7 @@ $PermissionLevel = $self->{PermissionLevel}; } else { # a different user, or no user was defined before - my $prettyCachedUserID = defined $cachedUserID ? "'$cachedUserID'" : "undefined"; + #my $prettyCachedUserID = defined $cachedUserID ? "'$cachedUserID'" : "undefined"; #warn "hasPermissions called with user '$userID', but cached user is $prettyCachedUserID. Accessing database.\n"; $PermissionLevel = $db->getPermissionLevel($userID); # checked } @@ -182,15 +183,29 @@ return 0; } + my $userRoles = $ce->{userRoles}; my $permissionLevels = $ce->{permissionLevels}; + if (exists $permissionLevels->{$activity}) { - if (defined $permissionLevels->{$activity}) { - return $permission_level >= $permissionLevels->{$activity}; + my $activity_role = $permissionLevels->{$activity}; + if (defined $activity_role) { + if (exists $userRoles->{$activity_role}) { + my $role_permlevel = $userRoles->{$activity_role}; + if (defined $role_permlevel) { + return $permission_level >= $role_permlevel; + } else { + warn "Role '$activity_role' has undefined permisison level -- assuming no permission.\n"; + return 0; + } + } else { + warn "Role '$activity_role' for activity '$activity' not found in \%userRoles -- assuming no permission.\n"; + return 0; + } } else { - return 0; # nobody has permission to do this + return 0; # undefiend $activity_role, no one has permission to perform $activity } } else { - warn "Activity '$activity' not found in %permissionLevels -- assuming no permission.\n"; + warn "Activity '$activity' not found in \%permissionLevels -- assuming no permission.\n"; return 0; } } |
From: Sam H. v. a. <we...@ma...> - 2005-09-30 20:28:30
|
Log Message: ----------- added manage_course_files activity. changed FileManger to use new activity. added hasPermissions() checks to Index and Contentgenerator to only show File Manger when user has permission. Modified Files: -------------- webwork2/conf: global.conf.dist webwork2/lib/WeBWorK: ContentGenerator.pm webwork2/lib/WeBWorK/ContentGenerator/Instructor: FileManager.pm Index.pm Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/global.conf.dist,v retrieving revision 1.143 retrieving revision 1.144 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.143 -r1.144 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -524,6 +524,7 @@ modify_set_def_files => "professor", modify_scoring_files => "professor", modify_problem_template_files => "professor", + manage_course_files => "professor", create_and_delete_courses => "professor", fix_course_databases => "professor", Index: ContentGenerator.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator.pm,v retrieving revision 1.149 retrieving revision 1.150 diff -Llib/WeBWorK/ContentGenerator.pm -Llib/WeBWorK/ContentGenerator.pm -u -r1.149 -r1.150 --- lib/WeBWorK/ContentGenerator.pm +++ lib/WeBWorK/ContentGenerator.pm @@ -646,8 +646,10 @@ print CGI::end_li(); ## Library browser - print CGI::li(CGI::a({href=>$self->systemLink($maker,params=>{ %displayOptions,})}, sp2nbsp($maker->name))) if $authz->hasPermissions($user, "modify_problem_sets"); - print CGI::li(CGI::a({href=>$self->systemLink($assigner,params=>{ %displayOptions,})}, sp2nbsp($assigner->name))) if $authz->hasPermissions($user, "assign_problem_sets"); + print CGI::li(CGI::a({href=>$self->systemLink($maker,params=>{ %displayOptions,})}, sp2nbsp($maker->name))) + if $authz->hasPermissions($user, "modify_problem_sets"); + print CGI::li(CGI::a({href=>$self->systemLink($assigner,params=>{ %displayOptions,})}, sp2nbsp($assigner->name))) + if $authz->hasPermissions($user, "assign_problem_sets"); ## Stats print CGI::li(CGI::a({href=>$self->systemLink($stats,params=>{ %displayOptions,})}, sp2nbsp($stats->name))); print CGI::start_li(); @@ -677,10 +679,13 @@ print CGI::end_ul(); print CGI::end_li(); ## Scoring tools - print CGI::li(CGI::a({href=>$self->systemLink($scoring,params=>{ %displayOptions,})}, sp2nbsp($scoring->name))) if $authz->hasPermissions($user, "score_sets"); + print CGI::li(CGI::a({href=>$self->systemLink($scoring,params=>{ %displayOptions,})}, sp2nbsp($scoring->name))) + if $authz->hasPermissions($user, "score_sets"); ## Email - print CGI::li(CGI::a({href=>$self->systemLink($mail,params=>{ %displayOptions,})}, sp2nbsp($mail->name))) if $authz->hasPermissions($user, "send_mail"); - print CGI::li(CGI::a({href=>$self->systemLink($fileMgr,params=>{ %displayOptions,})}, sp2nbsp($fileMgr->name))); + print CGI::li(CGI::a({href=>$self->systemLink($mail,params=>{ %displayOptions,})}, sp2nbsp($mail->name))) + if $authz->hasPermissions($user, "send_mail"); + print CGI::li(CGI::a({href=>$self->systemLink($fileMgr,params=>{ %displayOptions,})}, sp2nbsp($fileMgr->name))) + if $authz->hasPermissions($user, "manage_course_files"); #print CGI::li(CGI::a({href=>$self->systemLink($fileXfer)}, sp2nbsp($fileXfer->name))); print CGI::li( $self->helpMacro('instructor_links')); print CGI::end_ul(); Index: Index.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/Index.pm,v retrieving revision 1.49 retrieving revision 1.50 diff -Llib/WeBWorK/ContentGenerator/Instructor/Index.pm -Llib/WeBWorK/ContentGenerator/Instructor/Index.pm -u -r1.49 -r1.50 --- lib/WeBWorK/ContentGenerator/Instructor/Index.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Index.pm @@ -399,7 +399,10 @@ CGI::td(CGI::submit("edit_set_for_users", "Edit"). " one <b>set</b> for <b>users</b>"), CGI::td({-height=>4}), CGI::td(CGI::submit("email_users", "Email"). " your students"), - CGI::td(CGI::submit("transfer_files", "Transfer"). " course files"), + ($authz->hasPermissions($user, "manage_course_files") + ? CGI::td(CGI::submit("transfer_files", "Transfer"). " course files") + : () + ), ]) ) ) Index: FileManager.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm,v retrieving revision 1.15 retrieving revision 1.16 diff -Llib/WeBWorK/ContentGenerator/Instructor/FileManager.pm -Llib/WeBWorK/ContentGenerator/Instructor/FileManager.pm -u -r1.15 -r1.16 --- lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm +++ lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm @@ -58,11 +58,9 @@ my $authz = $r->authz; my $user = $r->param('user'); - unless ($authz->hasPermissions($user, "access_instructor_tools")) { - $self->addbadmessage("You aren't authorized to manage course files"); - return; - } - + # we don't need to return an error here, because body() will print an error for us :) + return unless $authz->hasPermissions($user, "manage_course_files"); + my $action = $r->param('action'); $self->Download if ($action && $action eq 'Download'); my $file = $r->param('download'); @@ -111,8 +109,8 @@ my $user = $r->param('user'); my $key = $r->param('key'); - return CGI::em("You are not authorized to access the instructor tools") - unless $authz->hasPermissions($user, "access_instructor_tools"); + return CGI::em("You are not authorized to manage course files") + unless $authz->hasPermissions($user, "manage_course_files"); $self->{pwd} = $self->checkPWD($r->param('pwd') || HOME); return CGI::em("You have specified an illegal working directory!") unless defined $self->{pwd}; |
From: dpvc v. a. <we...@ma...> - 2005-09-29 22:08:48
|
Log Message: ----------- Only add the 'dx' variable if it isn't already in the context. Modified Files: -------------- pg/macros: parserDifferenceQuotient.pl Revision Data ------------- Index: parserDifferenceQuotient.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserDifferenceQuotient.pl,v retrieving revision 1.3 retrieving revision 1.4 diff -Lmacros/parserDifferenceQuotient.pl -Lmacros/parserDifferenceQuotient.pl -u -r1.3 -r1.4 --- macros/parserDifferenceQuotient.pl +++ macros/parserDifferenceQuotient.pl @@ -48,10 +48,12 @@ # my $current = $$Value::context; my $context = main::Context($current->copy); - $context->{_variables}->{pattern} = $context->{_variables}->{namePattern} = - $dx . '|' . $context->{_variables}->{pattern}; - $context->update; - $context->variables->add($dx=>'Real'); + unless ($context->variables->get($dx)) { + $context->{_variables}->{pattern} = $context->{_variables}->{namePattern} = + $dx . '|' . $context->{_variables}->{pattern}; + $context->update; + $context->variables->add($dx=>'Real'); + } $q = bless $self->SUPER::new($formula), $class; $q->{isValue} = 1; $q->{isFormula} = 1; $q->{dx} = $dx; main::Context($current); # put back the original context; |
From: dpvc v. a. <we...@ma...> - 2005-09-29 22:02:19
|
Log Message: ----------- Fixed a problem with the automatic upgrading of object classes for when Value objects are subclassed but are not entered into the precedence list. Modified Files: -------------- pg/lib: Value.pm Revision Data ------------- Index: Value.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value.pm,v retrieving revision 1.48 retrieving revision 1.49 diff -Llib/Value.pm -Llib/Value.pm -u -r1.48 -r1.49 --- lib/Value.pm +++ lib/Value.pm @@ -411,7 +411,7 @@ return 0 unless Value::isValue($other); my $sprec = $$context->{precedence}{class($self)}; my $oprec = $$context->{precedence}{class($other)}; - return (defined($oprec) && $sprec < $oprec); + return (defined($sprec) && defined($oprec) && $sprec < $oprec); } sub promote {shift} |
From: Arnie P. v. a. <we...@ma...> - 2005-09-29 17:41:27
|
Log Message: ----------- Improve the label on submit button in single user mode. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator: Hardcopy.pm Revision Data ------------- Index: Hardcopy.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Hardcopy.pm,v retrieving revision 1.65 retrieving revision 1.66 diff -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -u -r1.65 -r1.66 --- lib/WeBWorK/ContentGenerator/Hardcopy.pm +++ lib/WeBWorK/ContentGenerator/Hardcopy.pm @@ -308,6 +308,7 @@ my $ss = $perm_multiuser ? "s" : ""; my $aa = $perm_multiuser ? " " : " a "; my $phrase_for_privileged_users = $perm_multiuser ? "to privileged users or" : ""; + my $button_label = $perm_multiuser ? "Generate hardcopy for selected sets and selected users" :"Generate hardcopy"; # print CGI::start_p(); # print "Select the homework set$ss for which to generate${aa}hardcopy version$ss."; @@ -383,7 +384,7 @@ CGI::td({colspan=>2, class=>"ButtonRow"}, CGI::submit( -name => "generate_hardcopy", - -value => "Generate hardcopy for selected sets and selected users", + -value => $button_label, #-style => "width: 45ex", ), ), |
From: Mike G. v. a. <we...@ma...> - 2005-09-29 01:58:00
|
Log Message: ----------- Allow making a local copy of a library problem with a single button push. The user interface is still cluttered. I think I'll rewrite the bottom button rows using radio buttons and a 'Take action' submit button as on the Classlist and Homework set editor pages. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm Revision Data ------------- Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.56 retrieving revision 1.57 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.56 -r1.57 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -237,6 +237,10 @@ $self->{action} = 'add_problem_to_set'; last SUBMIT_CASE; }; + ($submit_button eq 'Make local copy at: ') and do { + $self->{action} = 'make_local_copy'; + last SUBMIT_CASE; + }; # else die "Unrecognized submit command: |$submit_button|"; @@ -296,13 +300,14 @@ # FIXME: even with an error we still open a new page because of the target specified in the form - # Some cases do not need a redirect: save, refresh, save_as, add_problem_to_set, add_header_to_set + # Some cases do not need a redirect: save, refresh, save_as, add_problem_to_set, add_header_to_set,make_local_copy my $action = $self->{action}; return unless $action eq 'save' or $action eq 'refresh' or $action eq 'save_as' or $action eq 'add_problem_to_set' + or $action eq 'make_local_copy' or $action eq 'add_set_header_to_set'; @@ -360,6 +365,24 @@ } ); + } elsif ($action eq 'make_local_copy') { # redirect to myself + my $edit_level = $r->param("edit_level") || 0; + $edit_level++; + + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", + courseID => $courseName, setID => $setName, problemID => $problemNumber + ); + $viewURL = $self->systemLink($problemPage, + params=>{ + sourceFilePath => $sourceFilePath, + edit_level => $edit_level, + file_type => 'source_path_for_problem_file', + status_message => uri_escape($self->{status_message}) + + } + ); + + } elsif ( $action eq 'add_set_header_to_set') { my $targetSetName = $r->param('target_set'); my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", @@ -489,8 +512,12 @@ my $tempFilePath = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file) my $inputFilePath = $self->{inputFilePath}; # path to the file for input, (might be a .tmp file) my $protected_file = (not -w $inputFilePath ) and -e $inputFilePath; #FIXME -- let's try to insure that the input file always exists, even for revert. + + $self->addmessage($r->param('status_message') ||''); # record status messages carried over if this is a redirect $self->addbadmessage("Changes in this file have not yet been permanently saved.") if -r $tempFilePath; - $self->addbadmessage("This file |$inputFilePath| is protected! To edit this text you must first use 'Save As' to save it to another file.") if $protected_file; + + $self->addbadmessage("This file '$inputFilePath' is protected! ".CGI::br()."To edit this text you must either 'Make a local copy' of this problem, or + use 'Save As' to save it to another file.") if $protected_file; } @@ -624,7 +651,7 @@ CGI::end_form(); # Prepare add to set buttons my $add_files_to_set_buttons = ''; - if ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' ) { + if ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' or $file_type eq 'blank_problem' ) { $add_files_to_set_buttons .= CGI::submit(-value=>'Add problem to: ',-name=>'submit' ) ; } if ($file_type eq 'set_header' # set header or the problem number is not a regular positive number @@ -632,7 +659,7 @@ $add_files_to_set_buttons .=CGI::submit(-value=>'Make this the set header for: ',-name=>'submit' ); } # Add pop-up menu for the target set if either of these buttons has been revealed. - $add_files_to_set_buttons .= CGI::popup_menu(-name=>'target_set',-values=>\@allSetNames) if $add_files_to_set_buttons; + $add_files_to_set_buttons .= CGI::popup_menu(-name=>'target_set',-values=>\@allSetNames, -default=>$setName) if $add_files_to_set_buttons; return CGI::p($header), @@ -671,6 +698,11 @@ CGI::textfield(-name=>'save_to_new_file', -size=>40, -value=>""), ), + # allow one to make a local copy if the viewed file can't be edited. #FIXME the method for determining the localfilePath needs work + (-w $editFilePath) ? "" : CGI::p(CGI::hr(), + CGI::submit(-value=>'Make local copy at: ',-name=>'submit'), "[TMPL]/".determineLocalFilePath($editFilePath), + CGI::hidden(-name=>'local_copy_file_path', -value=>determineLocalFilePath($editFilePath) ) + ), CGI::end_form(); @@ -679,6 +711,20 @@ ################################################################################ # Utilities ################################################################################ + +# determineLocalFilePath constructs a local file path parallel to a library file path +# This is a subroutine, not a method +# +sub determineLocalFilePath { + my $path = shift; + if ($path =~ /Library/) { + $path =~ s|^.*?Library/||; # truncate the url up to a segment such as ...rochesterLibrary/....... + } else { # if its not in a library we'll just save it locally + $path = "new_problem_".rand(40); #l hope there aren't any collisions. + } + $path; + +} sub getFilePaths { my ($self, $setName, $problemNumber, $file_type, $TEMPFILESUFFIX) = @_; my $r = $self->r; @@ -736,8 +782,9 @@ ($file_type eq 'blank_problem') and do { $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{blankProblem}; - $self->addbadmessage("$editFilePath is blank problem template file and cannot be edited directly."); - $self->addbadmessage("Any changes you make will have to be saved as another file."); + $self->addbadmessage("$editFilePath is blank problem template file and should not be edited directly. " + ."First use 'Save as' to make a local copy, then add the file to the current problem set, then edit the file." + ); last CASE; }; @@ -960,6 +1007,68 @@ $self->{problemPath} = $outputFilePath; last ACTION_CASES; }; + ($action eq 'make_local_copy') and do { + my $new_file_name =$r->param('local_copy_file_path') || ''; + ################################################# + #bail unless this new file name has been defined + ################################################# + if ( $new_file_name !~ /\S/) { # need a non-blank file name + # setting $self->{failure} stops saving and any redirects + $do_not_save = 1; + warn "new file name is $new_file_name"; + $self->addbadmessage(CGI::p("Please specify a file to save to.")); + last ACTION_CASES; #stop processing + } + ################################################# + # grab the problemContents from the form in order to save it to a new permanent file + # later we will unlink (delete) the current temporary file + # store new permanent file name in the $self->problemPath for use in body + ################################################# + $problemContents = $r->param('problemContents'); + + ################################################# + # Rescue the user in case they forgot to end the file name with .pg + ################################################# + if($self->{file_type} eq 'problem' + or $self->{file_type} eq 'blank_problem' + or $self->{file_type} eq 'set_header') { + $new_file_name =~ s/\.pg$//; # remove it if it is there + $new_file_name .= '.pg'; # put it there + + } + + ################################################# + # check to prevent overwrites: + ################################################# + $outputFilePath = $ce->{courseDirs}->{templates} . '/' . + $new_file_name; + + if (defined $outputFilePath and -e $outputFilePath) { + # setting $do_not_save stops saving and any redirects + $do_not_save = 1; + $self->addbadmessage(CGI::p("There is already a file at [TMPL]/$new_file_name. File not saved.")); + } else { + $self->addgoodmessage("A local copy of $editFilePath is being made...") ; + } + $self->{problemPath} = $outputFilePath; + ################################################# + # if new file has been successfully saved change the file path name for the problem + ################################################# + unless ($do_not_save) { + my $problemRecord = $db->getGlobalProblem($setName,$problemNumber); + $problemRecord->source_file($new_file_name); + if ( $db->putGlobalProblem($problemRecord) ) { + $self->addgoodmessage("A local, editable, copy of $new_file_name has been made for problem $problemNumber.") ; + $self->{problemPath} = $outputFilePath; # define the file path for redirect + } else { + $self->addbadmessage("Unable to change the source file path for set $setName, problem $problemNumber. Unknown error."); + } + } + + + + last ACTION_CASES; + }; ($action eq 'add_problem_to_set') and do { my $sourceFile = $editFilePath; my $targetSetName = $r->param('target_set'); |
From: Mike G. v. a. <we...@ma...> - 2005-09-29 01:25:34
|
Log Message: ----------- Simplified the interface for users (who can only download one set at a time and can only download one user). It could be made slightly more general -- it currently implicitly assumes that either one can edit both multiple sets and multiple users or one can edit only a single user and a single set. Removed an error in the [edit] link when there is an error. The anchor HTML tags were wrapped two layers deep around the reference. Can probably still use some more tweaks in the interface. -- Mike Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator: Hardcopy.pm Revision Data ------------- Index: Hardcopy.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Hardcopy.pm,v retrieving revision 1.64 retrieving revision 1.65 diff -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -u -r1.64 -r1.65 --- lib/WeBWorK/ContentGenerator/Hardcopy.pm +++ lib/WeBWorK/ContentGenerator/Hardcopy.pm @@ -105,7 +105,7 @@ $self->{send_hardcopy} = 1; return; } - + # this should never happen, but apparently it did once (see bug #714), so we check for it die "Parameter 'user' not defined -- this should never happen" unless defined $userID; @@ -309,27 +309,44 @@ my $aa = $perm_multiuser ? " " : " a "; my $phrase_for_privileged_users = $perm_multiuser ? "to privileged users or" : ""; - print CGI::start_p(); - print "Select the homework set$ss for which to generate${aa}hardcopy version$ss."; - if ($authz->hasPermissions($userID, "download_hardcopy_multiuser")) { - print "You may also select multiple users from the users list. You will receive hardcopy for each (set, user) pair."; - } - print CGI::end_p(); +# print CGI::start_p(); +# print "Select the homework set$ss for which to generate${aa}hardcopy version$ss."; +# if ($authz->hasPermissions($userID, "download_hardcopy_multiuser")) { +# print "You may also select multiple users from the users list. You will receive hardcopy for each (set, user) pair."; +# } +# print CGI::end_p(); print CGI::start_form(-method=>"POST", -action=>$r->uri); print $self->hidden_authen_fields(); print CGI::hidden("in_hc_form", 1); + if ($perm_multiuser and $perm_multiset) { + print CGI::p("Select the homework sets for which to generate hardcopy versions. You may" + ." also select multiple users from the users list. You will receive hardcopy" + ." for each (set, user) pair."); + + print CGI::table({class=>"FormLayout"}, + CGI::Tr( + CGI::th("Users"), + CGI::th("Sets"), + ), + CGI::Tr( + CGI::td($scrolling_user_list), + CGI::td($scrolling_set_list), + ), + ); + } else { # single user mode + #FIXME -- do a better job of getting the set and the user when in the single set mode + my $selected_set_id = $r->param("selected_sets"); + my $selected_user_id = $Users[0]->user_id; + print CGI::hidden("selected_sets", $selected_set_id ), + CGI::hidden( "selected_users", $selected_user_id); + + print CGI::p("Download hardcopy of set ", $selected_set_id, " for ", $Users[0]->first_name, " ",$Users[0]->last_name,"?"); + + } print CGI::table({class=>"FormLayout"}, CGI::Tr( - CGI::th("Users"), - CGI::th("Sets"), - ), - CGI::Tr( - CGI::td($scrolling_user_list), - CGI::td($scrolling_set_list), - ), - CGI::Tr( CGI::td({colspan=>2, class=>"ButtonRow"}, CGI::small("You may choose to show any of the following data. Correct answers and solutions are only available $phrase_for_privileged_users after the answer date of the homework set."), CGI::br(), @@ -755,7 +772,7 @@ ); } else { # link for a real problem - $edit_url = CGI::a({href=>$self->systemLink($edit_urlpath)}, "Edit it"); + $edit_url = $self->systemLink($edit_urlpath); } if ($MergedProblem->problem_id == 0) { |
From: Mike G. v. a. <we...@ma...> - 2005-09-28 23:26:10
|
Log Message: ----------- Changed the wording of the initial course.conf file. Modified Files: -------------- webwork-modperl/lib/WeBWorK/Utils: CourseManagement.pm Revision Data ------------- Index: CourseManagement.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/Utils/CourseManagement.pm,v retrieving revision 1.25 retrieving revision 1.26 diff -Llib/WeBWorK/Utils/CourseManagement.pm -Llib/WeBWorK/Utils/CourseManagement.pm -u -r1.25 -r1.26 --- lib/WeBWorK/Utils/CourseManagement.pm +++ lib/WeBWorK/Utils/CourseManagement.pm @@ -891,11 +891,20 @@ } print $fh <<'EOF'; -# Feedback Mail Recipients (global value typically not defined) +# By default, feeback is sent to all users who have permission to +# receive_feedback. If this list is non-empty, feedback is also sent to the +# addresses specified here. # -# Defines recipients for feedback mail. If not defined, mail is sent to all -# instructors and TAs. +# * If you want to disable feedback altogether, leave this empty and set +# $permissionLevels{submit_feeback} = undef; +# This will cause the +# feedback button to go away as well. # +# * If you want to send email ONLY to addresses in this list, set +# $permissionLevels{receive_feedback} = undef; +# +# It's often useful to set this in the course.conf to change the behavior of +# feedback for a specific course. # global.conf values: EOF |
From: Mike G. v. a. <we...@ma...> - 2005-09-28 23:24:19
|
Log Message: ----------- Formatting change for readability Modified Files: -------------- webwork-modperl/conf: webwork.apache-config.dist Revision Data ------------- Index: webwork.apache-config.dist =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/conf/webwork.apache-config.dist,v retrieving revision 1.9 retrieving revision 1.10 diff -Lconf/webwork.apache-config.dist -Lconf/webwork.apache-config.dist -u -r1.9 -r1.10 --- conf/webwork.apache-config.dist +++ conf/webwork.apache-config.dist @@ -44,6 +44,15 @@ my $webwork_courses_dir = $ce->{webwork_courses_dir}; eval "use lib '$pg_dir/lib'"; die $@ if $@; +########################################## +# allows Webservice access to WeBWorK +########################################## + +# eval "use WebworkWebservice";die $@ if $@; #FIXME, is there another way to initialize this? +# eval "use RQP"; die $@ if $@; + +########################################## + $WeBWorK::SeedCE{webwork_dir} = $webwork_dir; # Between the line below and the "EOF" line are the three configuration stanzas @@ -68,6 +77,7 @@ # Define the location that is handled by the Apache::WeBWorK module, and tell # Perl where to find the libraries Apache::WeBWorK needs to run. # + <Location $webwork_url> SetHandler perl-script PerlHandler Apache::WeBWorK @@ -105,7 +115,7 @@ $PerlConfig .= <<EOF; -# XMLRPC installation +########## XMLRPC installation ########## # #<Location /mod_xmlrpc> # SetHandler perl-script @@ -116,7 +126,7 @@ # Allow from All #</Location> -# RQP installation +########## RQP installation ########## # ##<Location /rqp> ## SetHandler perl-script |
From: Sam H. v. a. <we...@ma...> - 2005-09-28 21:46:33
|
Log Message: ----------- fixed problem with `!' in set IDs, improved error reporting. the `!' poblem was caused by passing UserSets to ScrollingRecordList instead of passing GlobalSets. UserSets have a two-part key (user ID and set ID), and ScrollingRecordList concatenates them with a `!' to create the value names. This is a desireable behavior, but not one that I remembered. Whoops. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: Hardcopy.pm Revision Data ------------- Index: Hardcopy.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Hardcopy.pm,v retrieving revision 1.63 retrieving revision 1.64 diff -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -u -r1.63 -r1.64 --- lib/WeBWorK/ContentGenerator/Hardcopy.pm +++ lib/WeBWorK/ContentGenerator/Hardcopy.pm @@ -29,6 +29,7 @@ use Apache::Constants qw/:common REDIRECT/; use CGI qw//; use String::ShellQuote; +use WeBWorK::DB::Utils qw/user2global/; use WeBWorK::Debug; use WeBWorK::Form; use WeBWorK::HTML::ScrollingRecordList qw/scrollingRecordList/; @@ -58,16 +59,24 @@ ); # custom fields used in $self hash +# FOR HEAVEN'S SAKE, PLEASE KEEP THIS UP-TO-DATE! # # final_file_url # contains the URL of the final hardcopy file generated # set by generate_hardcopy(), used by pre_header_initialize() and body() +# # temp_file_map # reference to a hash mapping temporary file names to URL # set by pre_header_initialize(), used by body() +# # hardcopy_errors # reference to array containing HTML strings describing generation errors (and warnings) # used by add_errors(), get_errors(), get_errors_ref() +# +# at_least_one_problem_rendered_without_error +# set to a true value by write_problem_tex if it is able to sucessfully render +# a problem. checked by generate_hardcopy to determine whether to continue +# with the generation process. ################################################################################ # UI subroutines @@ -143,6 +152,9 @@ if (@userIDs and $userIDs[0] ne $eUserID and not $perm_multiuser) { $self->addbadmessage("You are not permitted to generate hardcopy for other users."); $validation_failed = 1; + # FIXME -- download_hardcopy_multiuser controls both whether a user can generate hardcopy + # that contains sets for multiple users AND whether she can generate hardcopy that contains + # sets for users other than herself. should these be separate permission levels? } unless ($validation_failed) { @@ -249,20 +261,27 @@ } # get sets for selection - my @Sets; + my @GlobalSets; if ($perm_multiuser) { - # if we're allowed to select sets for multiple users, get all sets - @Sets = $db->getGlobalSets($db->listGlobalSets); + # if we're allowed to select sets for multiple users, get all sets. + @GlobalSets = $db->getGlobalSets($db->listGlobalSets); } else { - # otherwise, only get the sets assigned to the effective user - @Sets = $db->getMergedSets(map [$eUserID,$_], $db->listUserSets($eUserID)); + # otherwise, only get the sets assigned to the effective user. + # note that we are getting GlobalSets, but using the list of UserSets assigned to the + # effective user. this is because if we pass UserSets to ScrollingRecordList it will + # give us composite IDs back, which is a pain in the ass to deal with. + @GlobalSets = $db->getGlobalSets($db->listUserSets($eUserID)); } # filter out unwanted sets - foreach my $i (0 .. $#Sets) { - my $Set = $Sets[$i]; - splice @Sets, $i, 1 unless $Set->open_date <= time or $perm_unopened; - splice @Sets, $i, 1 unless $Set->published or $perm_unpublished; + foreach my $i (0 .. $#GlobalSets) { + my $Set = $GlobalSets[$i]; + unless (defined $Set) { + warn "\$GlobalSets[\$i] not defined -- skipping"; + next; + } + splice @GlobalSets, $i, 1 unless $Set->open_date <= time or $perm_unopened; + splice @GlobalSets, $i, 1 unless $Set->published or $perm_unpublished; } my $scrolling_user_list = scrollingRecordList({ @@ -283,7 +302,7 @@ default_filters => ["all"], size => 20, multiple => $perm_multiset, - }, @Sets); + }, @GlobalSets); # we change the text a little bit depending on whether the user has multiuser privileges my $ss = $perm_multiuser ? "s" : ""; @@ -383,24 +402,44 @@ return; } - my $tex_file_name = "hardcopy.tex"; - my $tex_file_path = "$temp_dir_path/$tex_file_name"; - # do some error checking + unless (-e $temp_dir_path) { + $self->add_errors("Temporary directory '$temp_dir_path' does not exist, but creation didn't fail. This shouldn't happen."); + return; + } unless (-w $temp_dir_path) { - $self->add_errors("Temporary directory '$temp_dir_path' is not writeable: $!"); + $self->add_errors("Temporary directory '$temp_dir_path' is not writeable."); + $self->delete_temp_dir($temp_dir_path); return; } + my $tex_file_name = "hardcopy.tex"; + my $tex_file_path = "$temp_dir_path/$tex_file_name"; + # write TeX my $open_result = open my $FH, ">", $tex_file_path; unless ($open_result) { $self->add_errors("Failed to open file '$tex_file_path' for writing: $!"); + $self->delete_temp_dir($temp_dir_path); return; } $self->write_multiuser_tex($FH, $userIDsRef, $setIDsRef); close $FH; + # if no problems got rendered successfully, we can't continue + unless ($self->{at_least_one_problem_rendered_without_error}) { + $self->add_errors("No problems rendered. Can't continue."); + $self->delete_temp_dir($temp_dir_path); + return; + } + + # if no hardcopy.tex file was generated, fail now + unless (-e "$temp_dir_path/hardcopy.tex") { + $self->add_errors("'hardcopy.tex' not written to temporary directory '$temp_dir_path'. Can't continue."); + $self->delete_temp_dir($temp_dir_path); + return; + } + # determine base name of final file my $final_file_user = @$userIDsRef > 1 ? "multiuser" : $userIDsRef->[0]; my $final_file_set = @$setIDsRef > 1 ? "multiset" : $setIDsRef->[0]; @@ -437,9 +476,11 @@ # try to move the hardcopy file out of the temp directory # set $final_file_url accordingly my $final_file_final_path = "$temp_dir_parent_path/$final_file_name"; - my $mv_cmd = "/bin/mv " . shell_quote($final_file_path, $final_file_final_path); - if (system $mv_cmd) { - $self->add_errors("Failed to move hardcopy file '$final_file_name' from '$temp_dir_path' to '$temp_dir_parent_path': $!"); + my $mv_cmd = "2>&1 /bin/mv " . shell_quote($final_file_path, $final_file_final_path); + my $mv_out = readpipe $mv_cmd; + if ($?) { + $self->add_errors("Failed to move hardcopy file '$final_file_name' from '$temp_dir_path' to '$temp_dir_parent_path':" + .CGI::br().CGI::pre($mv_out)); $final_file_url = "$temp_dir_url/$final_file_name"; } else { $final_file_url = "$temp_dir_parent_url/$final_file_name"; @@ -447,10 +488,7 @@ # remove the temp directory if there are no errors unless ($self->get_errors or $PreserveTempFiles) { - my $rm_cmd = "/bin/rm -rf " . shell_quote($temp_dir_path); - if (system $rm_cmd) { - $self->add_errors("Failed to remove temp directory '$temp_dir_path': $!"); - } + $self->delete_temp_dir($temp_dir_path); } warn "Preserved temporary files in directory '$temp_dir_path'.\n" if $PreserveTempFiles; @@ -458,6 +496,21 @@ return $final_file_url, %temp_file_map; } +# helper function to remove temp dirs +sub delete_temp_dir { + my ($self, $temp_dir_path) = @_; + + my $rm_cmd = "2>&1 /bin/rm -rf " . shell_quote($temp_dir_path); + my $rm_out = readpipe $rm_cmd; + if ($?) { + $self->add_errors("Failed to remove temporary directory '$temp_dir_path':" + .CGI::br().CGI::pre($rm_out)); + return 0; + } else { + return 1; + } +} + # format subroutines # # assume that TeX source is located at $temp_dir_path/hardcopy.tex @@ -475,9 +528,10 @@ # try to rename tex file my $src_name = "hardcopy.tex"; my $dest_name = "$final_file_basename.tex"; - my $mv_cmd = "/bin/mv " . shell_quote("$temp_dir_path/$src_name", "$temp_dir_path/$dest_name"); - if (system $mv_cmd) { - $self->add_errors("Failed to rename '$src_name' to '$dest_name' in directory '$temp_dir_path': $!"); + my $mv_cmd = "2>&1 /bin/mv " . shell_quote("$temp_dir_path/$src_name", "$temp_dir_path/$dest_name"); + my $mv_out = readpipe $mv_cmd; + if ($?) { + $self->add_errors("Failed to rename '$src_name' to '$dest_name' in directory '$temp_dir_path':".CGI::br().CGI::pre($mv_out)); $final_file_name = $src_name; } else { $final_file_name = $dest_name; @@ -503,9 +557,10 @@ # try rename the pdf file my $src_name = "hardcopy.pdf"; my $dest_name = "$final_file_basename.pdf"; - my $mv_cmd = "/bin/mv " . shell_quote("$temp_dir_path/$src_name", "$temp_dir_path/$dest_name"); - if (system $mv_cmd) { - $self->add_errors("Failed to rename '$src_name' to '$dest_name' in directory '$temp_dir_path': $!"); + my $mv_cmd = "2>&1 /bin/mv " . shell_quote("$temp_dir_path/$src_name", "$temp_dir_path/$dest_name"); + my $mv_out = readpipe $mv_cmd; + if ($?) { + $self->add_errors("Failed to rename '$src_name' to '$dest_name' in directory '$temp_dir_path':".CGI::br().CGI::pre($mv_out)); $final_file_name = $src_name; } else { $final_file_name = $dest_name; @@ -553,7 +608,7 @@ # get user record my $TargetUser = $db->getUser($targetUserID); # checked unless ($TargetUser) { - $self->add_errors("Can't generate hardcopy for user $targetUserID -- no such user exists.\n"); + $self->add_errors("Can't generate hardcopy for user '$targetUserID' -- no such user exists.\n"); return; } @@ -578,17 +633,17 @@ # get set record my $MergedSet = $db->getMergedSet($TargetUser->user_id, $setID); # checked unless ($MergedSet) { - $self->add_errors("Can't generate hardcopy for set $setID for user ".$TargetUser->user_id." -- set is not assigned to that user."); + $self->add_errors("Can't generate hardcopy for set '$setID' for user '".$TargetUser->user_id."' -- set is not assigned to that user."); return; } # see if the *real* user is allowed to access this problem set if ($MergedSet->open_date > time and not $authz->hasPermissions($userID, "view_unopened_sets")) { - $self->add_errors("Can't generate hardcopy for set $setID for user ".$TargetUser->user_id." -- set is not yet open."); + $self->add_errors("Can't generate hardcopy for set '$setID' for user '".$TargetUser->user_id."' -- set is not yet open."); return; } if (not $MergedSet->published and not $authz->hasPermissions($userID, "view_unpublished_sets")) { - $self->addbadmessage("Can't generate hardcopy for set $setID for user ".$TargetUser->user_id." -- set has not been published."); + $self->addbadmessage("Can't generate hardcopy for set '$setID' for user '".$TargetUser->user_id."' -- set has not been published."); return; } @@ -633,7 +688,7 @@ # handle nonexistent problem unless ($MergedProblem) { - $self->add_errors("Can't generate hardcopy for problem $problemID in set ".$MergedSet->set_id." for user ".$MergedSet->user_id." -- problem does not exist in that set or is not assigned to that user."); + $self->add_errors("Can't generate hardcopy for problem '$problemID' in set '".$MergedSet->set_id."' for user '".$MergedSet->user_id."' -- problem does not exist in that set or is not assigned to that user."); return; } } elsif ($pgFile) { @@ -705,14 +760,14 @@ if ($MergedProblem->problem_id == 0) { $problem_name = "snippet"; - $problem_desc = $problem_name." ".$MergedProblem->source_file - ." for set ".$MergedProblem->set_id." and user " - .$MergedProblem->user_id; + $problem_desc = $problem_name." '".$MergedProblem->source_file + ."' for set '".$MergedProblem->set_id."' and user '" + .$MergedProblem->user_id."'"; } else { $problem_name = "problem"; - $problem_desc = $problem_name." ".$MergedProblem->problem_id - ." in set ".$MergedProblem->set_id." for user " - .$MergedProblem->user_id; + $problem_desc = $problem_name." '".$MergedProblem->problem_id + ."' in set '".$MergedProblem->set_id."' for user '" + .$MergedProblem->user_id."'"; } } @@ -734,6 +789,9 @@ return; } + # if we got here, there were no errors (because errors cause a return above) + $self->{at_least_one_problem_rendered_without_error} = 1; + print $FH $pg->{body_text}; # write the list of correct answers is appropriate @@ -758,7 +816,7 @@ my $tex = eval { readFile($file) }; if ($@) { - $self->add_errors("Failed to include TeX file $file: $@"); + $self->add_errors("Failed to include TeX file '$file': $@"); } else { print $FH $tex; } @@ -770,7 +828,6 @@ sub add_errors { my ($self, @errors) = @_; - #warn "add_errors(".join(", ", map("'$_'", @errors)).")"; push @{$self->{hardcopy_errors}}, @errors; } |
From: dpvc v. a. <we...@ma...> - 2005-09-28 20:40:31
|
Log Message: ----------- Fixed a bug where if you specified a variable other than dx, the division by zero check didn't work properly. Modified Files: -------------- pg/macros: parserDifferenceQuotient.pl Revision Data ------------- Index: parserDifferenceQuotient.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserDifferenceQuotient.pl,v retrieving revision 1.2 retrieving revision 1.3 diff -Lmacros/parserDifferenceQuotient.pl -Lmacros/parserDifferenceQuotient.pl -u -r1.2 -r1.3 --- macros/parserDifferenceQuotient.pl +++ macros/parserDifferenceQuotient.pl @@ -52,10 +52,10 @@ $dx . '|' . $context->{_variables}->{pattern}; $context->update; $context->variables->add($dx=>'Real'); - $dx = bless $self->SUPER::new($formula), $class; - $dx->{isValue} = 1; $dx->{isFormula} = 1; + $q = bless $self->SUPER::new($formula), $class; + $q->{isValue} = 1; $q->{isFormula} = 1; $q->{dx} = $dx; main::Context($current); # put back the original context; - return $dx; + return $q; } sub cmp_class {'a Difference Quotient'} @@ -66,10 +66,10 @@ )} sub cmp_postprocess { - my $self = shift; my $ans = shift; + my $self = shift; my $ans = shift; my $dx = $self->{dx}; return if $ans->{score} == 0 || $ans->{isPreview}; $main::__student_value__ = $ans->{student_value}; - my ($value,$err) = main::PG_restricted_eval('$__student_value__->reduce(dx=>0)'); + my ($value,$err) = main::PG_restricted_eval('$__student_value__->substitute('.$dx.'=>0)->reduce'); $self->cmp_Error($ans,"It looks like you didn't finish simplifying your answer") if $err && $err =~ m/division by zero/i; } |
From: dpvc v. a. <we...@ma...> - 2005-09-28 11:11:37
|
Log Message: ----------- Fixed typo in comments. Modified Files: -------------- pg/macros: parserDifferenceQuotient.pl Revision Data ------------- Index: parserDifferenceQuotient.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserDifferenceQuotient.pl,v retrieving revision 1.1 retrieving revision 1.2 diff -Lmacros/parserDifferenceQuotient.pl -Lmacros/parserDifferenceQuotient.pl -u -r1.1 -r1.2 --- macros/parserDifferenceQuotient.pl +++ macros/parserDifferenceQuotient.pl @@ -28,7 +28,7 @@ # ANS($df->cmp); # # Context()->variables->are(t=>'Real',a=>'Real'); -# ANS(DifferenceQuotient("-a/t(t+dt)","dt")->cmp); +# ANS(DifferenceQuotient("-a/[t(t+dt)]","dt")->cmp); # Context("Numeric"); |
From: Arnie P. v. a. <we...@ma...> - 2005-09-28 01:08:06
|
Log Message: ----------- Make email addresses active on the classlist page just as they are on the student progress page. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: UserList.pm Revision Data ------------- Index: UserList.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm,v retrieving revision 1.70 retrieving revision 1.71 diff -Llib/WeBWorK/ContentGenerator/Instructor/UserList.pm -Llib/WeBWorK/ContentGenerator/Instructor/UserList.pm -u -r1.70 -r1.71 --- lib/WeBWorK/ContentGenerator/Instructor/UserList.pm +++ lib/WeBWorK/ContentGenerator/Instructor/UserList.pm @@ -1391,6 +1391,14 @@ my $items = $properties->{items}; my $synonyms = $properties->{synonyms}; + if ($type eq "email") { + if ($value eq ' ') { + return $value;} + else { + return CGI::a({-href=>"mailto:$value"},$value); + } + } + if ($access eq "readonly") { return $value; } @@ -1398,7 +1406,7 @@ if ($type eq "number" or $type eq "text") { return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size}); } - + if ($type eq "enumerable") { my $matched = undef; # Whether a synonym match has occurred @@ -1521,20 +1529,21 @@ # User Fields foreach my $field ($User->NONKEYFIELDS) { - my $fieldName = "user." . $User->user_id . "." . $field, + my $fieldName = 'user.' . $User->user_id . '.' . $field, my $fieldValue = $User->$field; my %properties = %{ FIELD_PROPERTIES()->{$field} }; - $properties{access} = "readonly" unless $editMode; + $properties{access} = 'readonly' unless $editMode; + $properties{type} = 'email' if ($field eq 'email_address' and !$editMode and !$passwordMode); $fieldValue = $self->nbsp($fieldValue) unless $editMode; push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties)); } # PermissionLevel Fields foreach my $field ($PermissionLevel->NONKEYFIELDS) { - my $fieldName = "permission." . $PermissionLevel->user_id . "." . $field, + my $fieldName = 'permission.' . $PermissionLevel->user_id . '.' . $field, my $fieldValue = $PermissionLevel->$field; my %properties = %{ FIELD_PROPERTIES()->{$field} }; - $properties{access} = "readonly" unless $editMode; + $properties{access} = 'readonly' unless $editMode; $fieldValue = $self->nbsp($fieldValue) unless $editMode; push @tableCells, CGI::div({class=>$statusClass}, $self->fieldEditHTML($fieldName, $fieldValue, \%properties)); } |
From: Sam H. v. a. <we...@ma...> - 2005-09-27 23:32:44
|
Log Message: ----------- set a default value for hardcopy_format if it's not given. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: Hardcopy.pm Revision Data ------------- Index: Hardcopy.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Hardcopy.pm,v retrieving revision 1.62 retrieving revision 1.63 diff -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -u -r1.62 -r1.63 --- lib/WeBWorK/ContentGenerator/Hardcopy.pm +++ lib/WeBWorK/ContentGenerator/Hardcopy.pm @@ -103,6 +103,9 @@ if ($generate_hardcopy) { my $validation_failed = 0; + # set default format + $hardcopy_format = $HC_DEFAULT_FORMAT unless defined $hardcopy_format; + # make sure format is valid unless (grep { $_ eq $hardcopy_format } keys %HC_FORMATS) { $self->addbadmessage("'$hardcopy_format' is not a valid hardcopy format."); |
From: Sam H. v. a. <we...@ma...> - 2005-09-27 21:46:51
|
Log Message: ----------- pointless confusion, while humorous, is not good for maintainability. Modified Files: -------------- webwork2/lib/WeBWorK: Authen.pm Revision Data ------------- Index: Authen.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Authen.pm,v retrieving revision 1.43 retrieving revision 1.44 diff -Llib/WeBWorK/Authen.pm -Llib/WeBWorK/Authen.pm -u -r1.43 -r1.44 --- lib/WeBWorK/Authen.pm +++ lib/WeBWorK/Authen.pm @@ -517,12 +517,6 @@ } return 1; } - - # Whatever you do, don't delete this! - critical($r); - # One time, I deleted it, and my mother broke her back, my cat died, and - # the Pope got a tummy ache. When I replaced the line, I received eternal - # salvation and a check for USD 500. } # verifyProctor will return 1 if the proctor is who they say they are. It is @@ -625,7 +619,6 @@ } else { return 1; } - critical($r); # where does critical() come from? } 1; |