[LON-CAPA-cvs] cvs: loncom /interface lonhelper.pm
bowersj2
lon-capa-cvs@mail.lon-capa.org
Wed, 30 Apr 2003 15:18:36 -0000
This is a MIME encoded message
--bowersj21051715916
Content-Type: text/plain
bowersj2 Wed Apr 30 11:18:36 2003 EDT
Modified files:
/loncom/interface lonhelper.pm
Log:
Make it possible to create helpers manually, by exposing the functionality
necessary to do that.
--bowersj21051715916
Content-Type: text/plain
Content-Disposition: attachment; filename="bowersj2-20030430111836.txt"
Index: loncom/interface/lonhelper.pm
diff -u loncom/interface/lonhelper.pm:1.12 loncom/interface/lonhelper.pm:1.13
--- loncom/interface/lonhelper.pm:1.12 Thu Apr 17 13:21:24 2003
+++ loncom/interface/lonhelper.pm Wed Apr 30 11:18:36 2003
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# .helper XML handler to implement the LON-CAPA helper
#
-# $Id: lonhelper.pm,v 1.12 2003/04/17 17:21:24 bowersj2 Exp $
+# $Id: lonhelper.pm,v 1.13 2003/04/30 15:18:36 bowersj2 Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -84,6 +84,73 @@
necessary to put actual elements into the wizard. Documentation for each
of these elements follows.
+=head2 Creating a Helper With Code, Not XML
+
+In some situations, such as the printing wizard (see lonprintout.pm),
+writing the helper in XML would be too complicated, because of scope
+issues or the fact that the code actually outweighs the XML. It is
+possible to create a helper via code, though it is a little odd.
+
+Creating a helper via code is more like issuing commands to create
+a helper then normal code writing. For instance, elements will automatically
+be added to the last state created, so it's important to create the
+states in the correct order.
+
+First, create a new helper:
+
+ use Apache::lonhelper;
+
+ my $helper = Apache::lonhelper::new->("Helper Title");
+
+Next you'll need to manually add states to the helper:
+
+ Apache::lonhelper::state->new("STATE_NAME", "State's Human Title");
+
+You don't need to save a reference to it because all elements up until
+the next state creation will automatically be added to this state.
+
+Elements are created by populating the $paramHash in
+Apache::lonhelper::paramhash. To prevent namespace issues, retrieve
+a reference to that has with getParamHash:
+
+ my $paramHash = Apache::lonhelper::getParamHash();
+
+You will need to do this for each state you create.
+
+Populate the $paramHash with the parameters for the element you wish
+to add next; the easiest way to find out what those entries are is
+to read the code. Some common ones are 'variable' to record the variable
+to store the results in, and NEXTSTATE to record a next state transition.
+
+Then create your element:
+
+ $paramHash->{MESSAGETEXT} = "This is a message.";
+ Apache::lonhelper::message->new();
+
+The creation will take the $paramHash and bless it into a
+Apache::lonhelper::message object. To create the next element, you need
+to get a reference to the new, empty $paramHash:
+
+ $paramHash = Apache::lonhelper::getParamHash();
+
+and you can repeat creating elements that way. You can add states
+and elements as needed.
+
+See lonprintout.pm, subroutine printHelper for an example of this, where
+we dynamically add some states to prevent security problems, for instance.
+
+Normally the machinery in the XML format is sufficient; dynamically
+adding states can easily be done by wrapping the state in a <condition>
+tag. This should only be used when the code dominates the XML content,
+the code is so complicated that it is difficult to get access to
+all of the information you need because of scoping issues, or so much
+of the information used is persistent because would-be <exec> or
+<eval> blocks that using the {DATA} mechanism results in hard-to-read
+and -maintain code.
+
+It is possible to do some of the work with an XML fragment parsed by
+lonxml; again, see lonprintout.pm for an example.
+
=cut
package Apache::lonhelper;
@@ -122,10 +189,15 @@
# end of the element tag is located.
my $paramHash;
+# For debugging purposes, one can send a second parameter into this
+# function, the 'uri' of the helper you wish to have rendered, and
+# call this from other handlers.
sub handler {
my $r = shift;
- $ENV{'request.uri'} = $r->uri();
- my $filename = '/home/httpd/html' . $r->uri();
+ my $uri = shift;
+ if (!defined($uri)) { $uri = $r->uri(); }
+ $ENV{'request.uri'} = $uri;
+ my $filename = '/home/httpd/html' . $uri;
my $fh = Apache::File->new($filename);
my $file;
read $fh, $file, 100000000;
@@ -154,10 +226,24 @@
# xml parsing
&Apache::lonxml::xmlparse($r, 'helper', $file);
+ $helper->process();
+
$r->print($helper->display());
return OK;
}
+sub registerHelperTags {
+ for my $tagList (@helperTags) {
+ Apache::lonxml::register($tagList->[0], $tagList->[1]);
+ }
+}
+
+sub unregisterHelperTags {
+ for my $tagList (@helperTags) {
+ Apache::lonxml::deregister($tagList->[0], $tagList->[1]);
+ }
+}
+
sub start_helper {
my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
@@ -165,11 +251,9 @@
return '';
}
- for my $tagList (@helperTags) {
- Apache::lonxml::register($tagList->[0], $tagList->[1]);
- }
-
- $helper = Apache::lonhelper::helper->new($token->[2]{'title'});
+ registerHelperTags();
+
+ Apache::lonhelper::helper->new($token->[2]{'title'});
return '';
}
@@ -180,9 +264,7 @@
return '';
}
- for my $tagList (@helperTags) {
- Apache::lonxml::deregister($tagList->[0], $tagList->[1]);
- }
+ unregisterHelperTags();
return '';
}
@@ -194,11 +276,22 @@
return '';
}
- $state = Apache::lonhelper::state->new($token->[2]{'name'},
- $token->[2]{'title'});
+ Apache::lonhelper::state->new($token->[2]{'name'},
+ $token->[2]{'title'});
return '';
}
+# Use this to get the param hash from other files.
+sub getParamHash {
+ return $paramHash;
+}
+
+# Use this to get the helper, if implementing elements in other files
+# (like lonprintout.pm)
+sub getHelper {
+ return $helper;
+}
+
# don't need this, so ignore it
sub end_state {
return '';
@@ -287,6 +380,11 @@
# for an example.
$self->{DATA} = {};
+ $helper = $self;
+
+ # Establish the $paramHash
+ $paramHash = {};
+
bless($self, $class);
return $self;
}
@@ -347,23 +445,15 @@
$self->{STATES}{$stateName} = $state;
}
-# Done in four phases
-# 1: Do the post processing for the previous state.
-# 2: Do the preprocessing for the current state.
-# 3: Check to see if state changed, if so, postprocess current and move to next.
-# Repeat until state stays stable.
-# 4: Render the current state to the screen as an HTML page.
-sub display {
+sub process {
my $self = shift;
- my $result = "";
-
# Phase 1: Post processing for state of previous screen (which is actually
# the "current state" in terms of the helper variables), if it wasn't the
# beginning state.
if ($self->{STATE} ne "START" || $ENV{"form.SUBMIT"} eq "Next ->") {
my $prevState = $self->{STATES}{$self->{STATE}};
- $prevState->postprocess();
+ $prevState->postprocess();
}
# Note, to handle errors in a state's input that a user must correct,
@@ -374,11 +464,10 @@
my $startState = $self->{STATE};
my $state = $self->{STATES}{$startState};
- # Error checking; it is intended that the developer will have
- # checked all paths and the user can't see this!
+ # For debugging, print something here to determine if you're going
+ # to an undefined state.
if (!defined($state)) {
- $result .="Error! The state ". $startState ." is not defined.";
- return $result;
+ return;
}
$state->preprocess();
@@ -391,6 +480,21 @@
$state->preprocess();
}
+ return;
+}
+
+# 1: Do the post processing for the previous state.
+# 2: Do the preprocessing for the current state.
+# 3: Check to see if state changed, if so, postprocess current and move to next.
+# Repeat until state stays stable.
+# 4: Render the current state to the screen as an HTML page.
+sub display {
+ my $self = shift;
+
+ my $state = $self->{STATES}{$self->{STATE}};
+
+ my $result = "";
+
# Phase 4: Display.
my $stateTitle = $state->title();
my $bodytag = &Apache::loncommon::bodytag("$self->{TITLE}",'','');
@@ -430,9 +534,9 @@
$result .= "</center>\n";
}
- foreach my $key (keys %{$self->{VARS}}) {
- $result .= "|$key| -> " . $self->{VARS}->{$key} . "<br />";
- }
+ #foreach my $key (keys %{$self->{VARS}}) {
+ # $result .= "|$key| -> " . $self->{VARS}->{$key} . "<br />";
+ #}
$result .= <<FOOTER;
</td>
@@ -474,6 +578,8 @@
$helper->registerState($self);
+ $state = $self;
+
return $self;
}
@@ -517,7 +623,16 @@
}
}
+# Override the form if any element wants to.
+# two elements overriding the form will make a mess, but that should
+# be considered helper author error ;-)
sub overrideForm {
+ my $self = shift;
+ for my $element (@{$self->{ELEMENTS}}) {
+ if ($element->overrideForm()) {
+ return 1;
+ }
+ }
return 0;
}
@@ -616,6 +731,10 @@
return '';
}
+sub overrideForm {
+ return 0;
+}
+
sub process_multiple_choices {
my $self = shift;
my $formname = shift;
@@ -766,6 +885,11 @@
For example,
<choice computer='234-12-7312'>Bobby McDormik</choice>.
+ <choice> can take a parameter "eval", which if set to
+ a true value, will cause the contents of the tag to be
+ evaluated as it would be in an <eval> tag; see <eval> tag
+ below.
+
<choice> may optionally contain a 'nextstate' attribute, which
will be the state transisitoned to if the choice is made, if
the choice is not multichoice.
@@ -846,7 +970,9 @@
my $human = &Apache::lonxml::get_all_text('/choice',
$parser);
my $nextstate = $token->[2]{'nextstate'};
- push @{$paramHash->{CHOICES}}, [$human, $computer, $nextstate];
+ my $evalFlag = $token->[2]{'eval'};
+ push @{$paramHash->{CHOICES}}, [$human, $computer, $nextstate,
+ $evalFlag];
return '';
}
@@ -900,7 +1026,14 @@
$result .= " checked ";
$checked = 1;
}
- $result .= "/></td><td> " . $choice->[0] . "</td></tr>\n";
+ my $choiceLabel = $choice->[0];
+ if ($choice->[4]) { # if we need to evaluate this choice
+ $choiceLabel = "sub { my $helper = shift; my $state = shift;" .
+ $choiceLabel . "}";
+ $choiceLabel = eval($choiceLabel);
+ $choiceLabel = &$choiceLabel($helper, $self);
+ }
+ $result .= "/></td><td> " . $choiceLabel . "</td></tr>\n";
}
$result .= "</table>\n\n\n";
$result .= $buttons;
@@ -1192,6 +1325,9 @@
"}" returns a string representing what you want to have as the value. By
default, the value will be the resource ID of the object ($res->{ID}).
+=item * <mapurl>: If the URL of a map is given here, only that map
+ will be displayed, instead of the whole course.
+
=back
=cut
@@ -1203,7 +1339,8 @@
BEGIN {
&Apache::lonhelper::register('Apache::lonhelper::resource',
('resource', 'filterfunc',
- 'choicefunc', 'valuefunc'));
+ 'choicefunc', 'valuefunc',
+ 'mapurl'));
}
sub new {
@@ -1288,6 +1425,20 @@
sub end_valuefunc { return ''; }
+sub start_mapurl {
+ my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
+
+ if ($target ne 'helper') {
+ return '';
+ }
+
+ my $contents = Apache::lonxml::get_all_text('/mapurl',
+ $parser);
+ $paramHash->{MAP_URL} = eval $contents;
+}
+
+sub end_mapurl { return ''; }
+
# A note, in case I don't get to this before I leave.
# If someone complains about the "Back" button returning them
# to the previous folder state, instead of returning them to
@@ -1313,6 +1464,7 @@
my $filterFunc = $self->{FILTER_FUNC};
my $choiceFunc = $self->{CHOICE_FUNC};
my $valueFunc = $self->{VALUE_FUNC};
+ my $mapUrl = $self->{MAP_URL};
# Create the composite function that renders the column on the nav map
# have to admit any language that lets me do this can't be all bad
@@ -1341,9 +1493,9 @@
&Apache::lonnavmaps::render( { 'cols' => [$renderColFunc,
Apache::lonnavmaps::resource()],
'showParts' => 0,
- 'url' => $helper->{URL},
'filterFunc' => $filterFunc,
- 'resource_no_folder_link' => 1 }
+ 'resource_no_folder_link' => 1,
+ 'iterator_map' => $mapUrl }
);
return $result;
@@ -1929,7 +2081,148 @@
Apache::lonhelper::message->new();
}
+1;
+
+package Apache::lonhelper::parmwizfinal;
+
+# This is the final state for the parmwizard. It is not generally useful,
+# so it is not perldoc'ed. It does its own processing.
+# It is represented with <parmwizfinal />, and
+# should later be moved to lonparmset.pm .
+
+no strict;
+@ISA = ('Apache::lonhelper::element');
+use strict;
+BEGIN {
+ &Apache::lonhelper::register('Apache::lonhelper::parmwizfinal',
+ ('parmwizfinal'));
+}
+
+use Time::localtime;
+
+sub new {
+ my $ref = Apache::lonhelper::choices->new();
+ bless ($ref);
+}
+
+sub start_parmwizfinal { return ''; }
+
+sub end_parmwizfinal {
+ my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
+
+ if ($target ne 'helper') {
+ return '';
+ }
+ Apache::lonhelper::parmwizfinal->new();
+}
+
+# Renders a form that, when submitted, will form the input to lonparmset.pm
+sub render {
+ my $self = shift;
+ my $vars = $helper->{VARS};
+
+ # FIXME: Unify my designators with the standard ones
+ my %dateTypeHash = ('open_date' => "Opening Date",
+ 'due_date' => "Due Date",
+ 'answer_date' => "Answer Date");
+ my %parmTypeHash = ('open_date' => "0_opendate",
+ 'due_date' => "0_duedate",
+ 'answer_date' => "0_answerdate");
+
+ my $result = "<form name='wizform' method='get' action='/adm/parmset'>\n";
+ $result .= '<p>Confirm that this information is correct, then click "Finish Wizard" to complete setting the parameter.<ul>';
+ my $affectedResourceId = "";
+ my $parm_name = $parmTypeHash{$vars->{ACTION_TYPE}};
+ my $level = "";
+
+ # Print the type of manipulation:
+ $result .= '<li>Setting the <b>' . $dateTypeHash{$vars->{ACTION_TYPE}}
+ . "</b></li>\n";
+ if ($vars->{ACTION_TYPE} eq 'due_date' ||
+ $vars->{ACTION_TYPE} eq 'answer_date') {
+ # for due dates, we default to "date end" type entries
+ $result .= "<input type='hidden' name='recent_date_end' " .
+ "value='" . $vars->{PARM_DATE} . "' />\n";
+ $result .= "<input type='hidden' name='pres_value' " .
+ "value='" . $vars->{PARM_DATE} . "' />\n";
+ $result .= "<input type='hidden' name='pres_type' " .
+ "value='date_end' />\n";
+ } elsif ($vars->{ACTION_TYPE} eq 'open_date') {
+ $result .= "<input type='hidden' name='recent_date_start' ".
+ "value='" . $vars->{PARM_DATE} . "' />\n";
+ $result .= "<input type='hidden' name='pres_value' " .
+ "value='" . $vars->{PARM_DATE} . "' />\n";
+ $result .= "<input type='hidden' name='pres_type' " .
+ "value='date_start' />\n";
+ }
+
+ # Print the granularity, depending on the action
+ if ($vars->{GRANULARITY} eq 'whole_course') {
+ $result .= '<li>for <b>all resources in the course</b></li>';
+ $level = 9; # general course, see lonparmset.pm perldoc
+ $affectedResourceId = "0.0";
+ } elsif ($vars->{GRANULARITY} eq 'map') {
+ my $navmap = Apache::lonnavmaps::navmap->new(
+ $ENV{"request.course.fn"}.".db",
+ $ENV{"request.course.fn"}."_parms.db", 0, 0);
+ my $res = $navmap->getById($vars->{RESOURCE_ID});
+ my $title = $res->compTitle();
+ $navmap->untieHashes();
+ $result .= "<li>for the map named <b>$title</b></li>";
+ $level = 8;
+ $affectedResourceId = $vars->{RESOURCE_ID};
+ } else {
+ my $navmap = Apache::lonnavmaps::navmap->new(
+ $ENV{"request.course.fn"}.".db",
+ $ENV{"request.course.fn"}."_parms.db", 0, 0);
+ my $res = $navmap->getById($vars->{RESOURCE_ID});
+ my $title = $res->compTitle();
+ $navmap->untieHashes();
+ $result .= "<li>for the resource named <b>$title</b></li>";
+ $level = 7;
+ $affectedResourceId = $vars->{RESOURCE_ID};
+ }
+
+ # Print targets
+ if ($vars->{TARGETS} eq 'course') {
+ $result .= '<li>for <b>all students in course</b></li>';
+ } elsif ($vars->{TARGETS} eq 'section') {
+ my $section = $vars->{SECTION_NAME};
+ $result .= "<li>for section <b>$section</b></li>";
+ $level -= 3;
+ $result .= "<input type='hidden' name='csec' value='" .
+ HTML::Entities::encode($section) . "' />\n";
+ } else {
+ # FIXME: This is probably wasteful! Store the name!
+ my $classlist = Apache::loncoursedata::get_classlist();
+ my $name = $classlist->{$vars->{USER_NAME}}->[6];
+ $result .= "<li>for <b>$name</b></li>";
+ $level -= 6;
+ my ($uname, $udom) = split /:/, $vars->{USER_NAME};
+ $result .= "<input type='hidden' name='uname' value='".
+ HTML::Entities::encode($uname) . "' />\n";
+ $result .= "<input type='hidden' name='udom' value='".
+ HTML::Entities::encode($udom) . "' />\n";
+ }
+
+ # Print value
+ $result .= "<li>to <b>" . ctime($vars->{PARM_DATE}) . "</b> (" .
+ Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE})
+ . ")</li>\n";
+
+ # print pres_marker
+ $result .= "\n<input type='hidden' name='pres_marker'" .
+ " value='$affectedResourceId&$parm_name&$level' />\n";
+
+ $result .= "<br /><br /><center><input type='submit' value='Finish Helper' /></center></form>\n";
+
+ return $result;
+}
+
+sub overrideForm {
+ return 1;
+}
1;
--bowersj21051715916--