version 1.3, 2003/01/30 19:34:24
|
version 1.7, 2003/02/13 21:00:11
|
Line 5 package Apache::lonwizard;
|
Line 5 package Apache::lonwizard;
|
|
|
use Apache::Constants qw(:common :http); |
use Apache::Constants qw(:common :http); |
use Apache::loncommon; |
use Apache::loncommon; |
|
use Apache::lonnet; |
|
|
=head1 lonwizard - HTML "Wizard" framework for LON-CAPA |
=head1 lonwizard - HTML "Wizard" framework for LON-CAPA |
|
|
Line 102 sub new {
|
Line 103 sub new {
|
|
|
=cut |
=cut |
|
|
|
# Sometimes the wizard writer will want to use the result of the previous |
|
# state to change the text of the next state. In order to do that, it |
|
# has to be done during the declaration of the states, or it won't be |
|
# available. Therefore, basic form processing must occur before the |
|
# actual display routine is called and the actual pre-process is called, |
|
# or it won't be available. |
|
# This also factors common code out of the preprocess calls. |
sub declareVars { |
sub declareVars { |
my $self = shift; |
my $self = shift; |
my $varlist = shift; |
my $varlist = shift; |
Line 118 sub declareVars {
|
Line 126 sub declareVars {
|
{ |
{ |
$self->{VARS}->{$element} = $ENV{$envname}; |
$self->{VARS}->{$element} = $ENV{$envname}; |
} |
} |
|
|
|
# If there's an incoming form submission, use that |
|
my $envname = "form." . $element . ".forminput"; |
|
if (defined ($ENV{$envname})) { |
|
$self->{VARS}->{$element} = $ENV{$envname}; |
|
} |
} |
} |
} |
} |
|
|
Line 253 HEADER
|
Line 267 HEADER
|
} |
} |
else |
else |
{ |
{ |
|
$result .= '<input name="back" type="button" '; |
|
$result .= 'value="<- Previous" onclick="history.go(-1)" /> '; |
$result .= '<input name="SUBMIT" type="submit" value="Next ->" />'; |
$result .= '<input name="SUBMIT" type="submit" value="Next ->" />'; |
} |
} |
$result .= "</center>\n"; |
$result .= "</center>\n"; |
Line 310 sub setVar {
|
Line 326 sub setVar {
|
|
|
=pod |
=pod |
|
|
|
=item B<queryStringVars>(): Returns a string representing the current state of the wizard, suitable for use directly as part of a query string. (See resource_state for an example.) |
|
|
|
=cut |
|
|
|
sub queryStringVars { |
|
my $self = shift; |
|
|
|
my @queryString = (); |
|
|
|
for my $varname (keys %{$self->{VARS}}) { |
|
push @queryString, Apache::lonnet::escape($varname) . "=" . |
|
Apache::lonnet::escape($self->{VARS}{$varname}); |
|
} |
|
push @queryString, 'CURRENT_STATE=' . Apache::lonnet::escape($self->{STATE}); |
|
push @queryString, 'RETURN_PAGE=' . Apache::lonnet::escape($self->{RETURN_PAGE}); |
|
|
|
return join '&', @queryString; |
|
} |
|
|
|
=pod |
|
|
=item B<setDone>(): If a state calls this, the wizard will consider itself completed. The state should display a friendly "Done" message, and the wizard will display a link returning the user to the invoking page, rather then a "Next" button. |
=item B<setDone>(): If a state calls this, the wizard will consider itself completed. The state should display a friendly "Done" message, and the wizard will display a link returning the user to the invoking page, rather then a "Next" button. |
|
|
=cut |
=cut |
Line 342 sub handler {
|
Line 379 sub handler {
|
$r->rflush(); |
$r->rflush(); |
|
|
my $mes = <<WIZBEGIN; |
my $mes = <<WIZBEGIN; |
<p>This wizard will allow you to</p> |
<p>This wizard will allow you to <b>set open, due, and answer dates for problems</b>. You will be asked to select a problem, what kind of date you want to set, and for whom the date should be effective.</p> |
|
|
<ul> |
|
<li>Change assignment parameters, such as due date or open date...</li> |
|
<li>... for a whole class</li> |
|
<li>... for a whole section</li> |
|
<li>... for an individual student</li> |
|
<li>... by folder</li> |
|
<li>... by individual assignment</li> |
|
</ul> |
|
|
|
<p>After the wizard is done, you will be shown where in the advanced interface you would have gone to change the parameter you have chosen, so in the future you can do it directly.</p> |
<p>After the wizard is done, you will be shown where in the advanced interface you would have gone to change the parameter you have chosen, so in the future you can do it directly.</p> |
|
|
|
<p>Press <b>Next -></b> to begin, or select <b><- Previous</b> to go back to the previous screen.</p> |
WIZBEGIN |
WIZBEGIN |
|
|
my $wizard = Apache::lonwizard->new("Course Parameter Wizard"); |
my $wizard = Apache::lonwizard->new("Course Parameter Wizard"); |
$wizard->declareVars(['ACTION_TYPE', 'GRANULARITY', 'TARGETS', 'PARM_DATE', 'RESOURCE_ID', 'USER_NAME', 'SECTION_NAME']); |
$wizard->declareVars(['ACTION_TYPE', 'GRANULARITY', 'TARGETS', 'PARM_DATE', 'RESOURCE_ID', 'USER_NAME', 'SECTION_NAME']); |
my %dateTypeHash = ('open_date' => "Opening Date", |
my %dateTypeHash = ('open_date' => "opening date", |
'due_date' => "Due Date", |
'due_date' => "due date", |
'answer_date' => "Answer Date"); |
'answer_date' => "answer date"); |
|
my %levelTypeHash = ('whole_course' => "all problems in the course", |
|
'map' => 'the selected folder', |
|
'resource' => 'the selected problem'); |
|
|
Apache::lonwizard::message_state->new($wizard, "START", "Welcome to the Assignment Parameter Wizard", $mes, "CHOOSE_ACTION"); |
Apache::lonwizard::message_state->new($wizard, "START", "Welcome to the Assignment Parameter Wizard", $mes, "CHOOSE_LEVEL"); |
Apache::lonwizard::switch_state->new($wizard, "CHOOSE_ACTION", "What do you want to do?", "ACTION_TYPE", [ |
Apache::lonwizard::switch_state->new($wizard, "CHOOSE_LEVEL", "Which Problem or Problems?", "GRANULARITY", [ |
["open_date", "Set an Open Date for a problem", "CHOOSE_LEVEL"], |
["whole_course", "<b>Every problem</b> in the course", "CHOOSE_ACTION"], |
["due_date", "Set a Due Date for a problem", "CHOOSE_LEVEL"], |
["map", "Every problem in a particular <b>folder</b>", "CHOOSE_FOLDER"], |
["answer_date", "Set an Answer Open Date for a problem", "CHOOSE_LEVEL" ] ]); |
["resource", "One particular <b>problem</b>", "CHOOSE_RESOURCE"]], |
Apache::lonwizard::switch_state->new($wizard, "CHOOSE_LEVEL", "Parameter Granularity", "GRANULARITY", [ |
"Which problems do you wish to change a date for?"); |
["whole_course", "Set for Whole Course", "CHOOSE_STUDENT_LEVEL"], |
Apache::lonwizard::resource_choice->new($wizard, "CHOOSE_FOLDER", "Select Folder", "Select the folder you wish to set the date for:", "", "CHOOSE_ACTION", "RESOURCE_ID", sub {my $res = shift; return $res->is_map();}); |
["map", "Set for a Folder/Map", "CHOOSE_FOLDER"], |
Apache::lonwizard::resource_choice->new($wizard, "CHOOSE_RESOURCE", "Select Resource", "", "", "CHOOSE_ACTION", "RESOURCE_ID", sub {my $res = shift; return $res->is_map() || $res->is_problem();}, sub {my $res = shift; return $res->is_problem(); }); |
["resource", "Set for a Particular Problem", "CHOOSE_RESOURCE"]], |
my $levelType = $levelTypeHash{$wizard->{VARS}->{GRANULARITY}}; |
"How general should this setting be?"); |
Apache::lonwizard::switch_state->new($wizard, "CHOOSE_ACTION", "Parameter Type", "ACTION_TYPE", [ |
Apache::lonwizard::resource_choice->new($wizard, "CHOOSE_FOLDER", "Select Folder", "", "", "CHOOSE_STUDENT_LEVEL", "RESOURCE_ID", sub {my $res = shift; return $res->is_map();}); |
["open_date", "Set an <b>open date</b>", "CHOOSE_DATE"], |
Apache::lonwizard::resource_choice->new($wizard, "CHOOSE_RESOURCE", "Select Resource", "", "", "CHOOSE_STUDENT_LEVEL", "RESOURCE_ID", sub {my $res = shift; return $res->is_map() || $res->is_problem();}, sub {my $res = shift; return $res->is_problem(); }); |
["due_date", "Set a <b>due date</b>", "CHOOSE_DATE"], |
Apache::lonwizard::switch_state->new($wizard, "CHOOSE_STUDENT_LEVEL", "Parameter Targets", "TARGETS", [ |
["answer_date", "Set an <b>answer open date</b>", "CHOOSE_DATE" ] ], |
["course", "Set for All Students in Course", "CHOOSE_DATE"], |
"What parameters do you want to set for $levelType?"); |
["section", "Set for Section", "CHOOSE_SECTION"], |
|
["student", "Set for an Individual Student", "CHOOSE_STUDENT"]], |
|
"Whom should this setting affect?"); |
|
|
|
my $dateType = $dateTypeHash{$wizard->{VARS}->{ACTION_TYPE}}; |
my $dateType = $dateTypeHash{$wizard->{VARS}->{ACTION_TYPE}}; |
Apache::lonwizard::choose_section->new($wizard, "CHOOSE_SECTION", "Select Section", "Please select the section you wish to set the $dateType for:", "", "CHOOSE_DATE", "SECTION_NAME"); |
Apache::lonwizard::date_state->new($wizard, "CHOOSE_DATE", "Set Date", "PARM_DATE", "CHOOSE_STUDENT_LEVEL", "What should the $dateType be set to?"); |
Apache::lonwizard::choose_student->new($wizard, "CHOOSE_STUDENT", "Select Student", "Please select the student you wish to set the $dateType for:", "", "CHOOSE_DATE", "USER_NAME"); |
Apache::lonwizard::switch_state->new($wizard, "CHOOSE_STUDENT_LEVEL", "Students Affected", "TARGETS", [ |
Apache::lonwizard::date_state->new($wizard, "CHOOSE_DATE", "Set Date", "PARM_DATE", "FINISH", "What should the $dateType be set to?"); |
["course", ". . . for <b>all students</b> in the course", "FINISH"], |
|
["section", ". . . for a particular <b>section</b>", "CHOOSE_SECTION"], |
|
["student", ". . . for an individual <b>student</b>", "CHOOSE_STUDENT"]], |
|
"Set $dateType of $levelType for. . ."); |
|
|
|
Apache::lonwizard::choose_section->new($wizard, "CHOOSE_SECTION", "Select Section", "Please select the section you wish to set the $dateType for:", "", "FINISH", "SECTION_NAME"); |
|
Apache::lonwizard::choose_student->new($wizard, "CHOOSE_STUDENT", "Select Student", "Please select the student you wish to set the $dateType for:", "", "FINISH", "USER_NAME"); |
Apache::lonwizard::parmwizfinal->new($wizard, "FINISH", "Confirm Selection"); |
Apache::lonwizard::parmwizfinal->new($wizard, "FINISH", "Confirm Selection"); |
|
|
$r->print($wizard->display()); |
$r->print($wizard->display()); |
Line 405 It is importent to remember when constru
|
Line 440 It is importent to remember when constru
|
|
|
None of the pre-packaged states correctly handle there being B<no> input, as the wizard does not currently have any protection against errors in the states themselves. (The closest thing you can do is set the wizard to be done and display an error message, which should be adequate.) |
None of the pre-packaged states correctly handle there being B<no> input, as the wizard does not currently have any protection against errors in the states themselves. (The closest thing you can do is set the wizard to be done and display an error message, which should be adequate.) |
|
|
|
By default, the wizard framework will take form elements of the form {VAR_NAME}.forminput and automatically insert the contents of that form element into the wizard variable {VAR_NAME}. You only need to use postprocess to do something fancy if that is not sufficient, for instance, processing a multi-element selection. (See resource choice for an example of that.) |
|
|
=head2 lonwizstate methods |
=head2 lonwizstate methods |
|
|
These methods should be overridden in derived states, except B<new> which may be sufficient. |
These methods should be overridden in derived states, except B<new> which may be sufficient. |
Line 639 sub postprocess {
|
Line 676 sub postprocess {
|
my $wizard = $self->{WIZARD}; |
my $wizard = $self->{WIZARD}; |
my $formvalue = $ENV{'form.' . $self->{VAR_NAME} . '.forminput'}; |
my $formvalue = $ENV{'form.' . $self->{VAR_NAME} . '.forminput'}; |
if ($formvalue) { |
if ($formvalue) { |
$wizard->setVar($self->{VAR_NAME}, $formvalue); |
# Value already stored by Wizard |
$wizard->changeState($self->{NEXT_STATE}); |
$wizard->changeState($self->{NEXT_STATE}); |
} else { |
} else { |
$self->{ERROR_MSG} = "Can't continue the wizard because you must make" |
$self->{ERROR_MSG} = "Can't continue the wizard because you must make" |
Line 721 sub render {
|
Line 758 sub render {
|
} |
} |
|
|
sub postprocess { |
sub postprocess { |
|
# Value already stored by wizard |
my $self = shift; |
my $self = shift; |
my $wizard = $self->{WIZARD}; |
my $wizard = $self->{WIZARD}; |
my $chosenValue = $ENV{"form." . $self->{VAR_NAME} . '.forminput'}; |
my $chosenValue = $ENV{"form." . $self->{VAR_NAME} . '.forminput'}; |
$wizard->setVar($self->{VAR_NAME}, $chosenValue) |
|
if (defined ($self->{VAR_NAME})); |
|
|
|
foreach my $choice (@{$self->{CHOICE_LIST}}) |
foreach my $choice (@{$self->{CHOICE_LIST}}) |
{ |
{ |
Line 834 sub render {
|
Line 870 sub render {
|
} else { |
} else { |
$result .= "<option value='$i'>"; |
$result .= "<option value='$i'>"; |
} |
} |
$result .= @months[$i] . "\n"; |
$result .= $months[$i] . "\n"; |
} |
} |
$result .= "</select>\n"; |
$result .= "</select>\n"; |
|
|
Line 1081 Note this state will not automatically a
|
Line 1117 Note this state will not automatically a
|
|
|
=over 4 |
=over 4 |
|
|
=item overriddent method B<new>(parentLonWizReference, stateName, stateTitle, messageBefore, messageAfter, nextState, varName, filterFunction, choiceFunction): messageBefore and messageAfter appear before and after the state choice, respectively. nextState is the state to proceed to after the choice. varName is the wizard variable to store the choice in. |
=item overriddent method B<new>(parentLonWizReference, stateName, stateTitle, messageBefore, messageAfter, nextState, varName, filterFunction, choiceFunction, multichoice): messageBefore and messageAfter appear before and after the state choice, respectively. nextState is the state to proceed to after the choice. varName is the wizard variable to store the choice in. |
|
|
filterFunction is a function reference that receives the current resource as an argument, and returns 1 if it should be displayed, and 0 if it should not be displayed. By default, the class will use sub {return 1;}, which will show all resources. choiceFunction is a reference to a function that receives the resource object as a parameter and returns 1 if it should be a *selectable choice*, and 0 if not. By default, this is the same as the filterFunction, which means all displayed choices will be choosable. See parm wizard for an example of this in the resource selection routines. |
filterFunction is a function reference that receives the current resource as an argument, and returns 1 if it should be displayed, and 0 if it should not be displayed. By default, the class will use sub {return 1;}, which will show all resources. choiceFunction is a reference to a function that receives the resource object as a parameter and returns 1 if it should be a *selectable choice*, and 0 if not. By default, this is the same as the filterFunction, which means all displayed choices will be choosable. See parm wizard for an example of this in the resource selection routines. |
|
|
|
multichoice specifies whether the state should provide radio buttons, allowing the user one choice, or checkboxes, allowing the user multiple choices, and automatically including some convenience buttons the user can choose (like "Check All" and "Uncheck All"), implemented with Javascript. Defaults to false, allow just one choice. |
|
|
=back |
=back |
|
|
=cut |
=cut |
Line 1110 sub new {
|
Line 1148 sub new {
|
if (!defined($self->{CHOICE_FUNC})) { |
if (!defined($self->{CHOICE_FUNC})) { |
$self->{CHOICE_FUNC} = $self->{FILTER_FUNC}; |
$self->{CHOICE_FUNC} = $self->{FILTER_FUNC}; |
} |
} |
|
$self->{MULTICHOICE} = shift; |
|
if (!defined($self->{MULTICHOICE})) { |
|
$self->{MULTICHOICE} = 0; |
|
} |
} |
} |
|
|
sub postprocess { |
sub postprocess { |
my $self = shift; |
my $self = shift; |
my $wizard = $self->{WIZARD}; |
my $wizard = $self->{WIZARD}; |
my $chosenValue = $ENV{"form." . $self->{VAR_NAME} . ".forminput"}; |
|
$wizard->setVar($self->{VAR_NAME}, $chosenValue) |
# If we were just manipulating a folder, do not proceed to the |
if (defined($self->{VAR_NAME})); |
# next state |
|
if ($ENV{'form.folderManip'}) { |
|
return; |
|
} |
|
|
|
if (!$ENV{'form.' . $self->{VAR_NAME} . '.forminput'}) { |
|
$self->{ERROR_MSG} = "Can't continue wizard because you must ". |
|
"select a resource."; |
|
return; |
|
} |
|
|
|
|
|
# Value stored by wizard framework |
|
|
$wizard->changeState($self->{NEXT_STATE}); |
$wizard->changeState($self->{NEXT_STATE}); |
} |
} |
|
|
sub render { |
sub render { |
my $self = shift; |
my $self = shift; |
|
my $wizard = $self->{WIZARD}; |
my $result = ""; |
my $result = ""; |
my $var = $self->{VAR_NAME}; |
my $var = $self->{VAR_NAME}; |
my $curVal = $self->{WIZARD}->{VARS}->{$var}; |
my $curVal = $self->{WIZARD}->{VARS}->{$var}; |
|
my $vals = {}; |
|
if ($curVal =~ /,/) { # multiple choices |
|
foreach (split /,/, $curVal) { |
|
$vals->{$_} = 1; |
|
} |
|
} else { |
|
$vals->{$curVal} = 1; |
|
} |
|
|
|
if (defined $self->{ERROR_MSG}) { |
|
$result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />'; |
|
} |
|
|
$result .= $self->{MESSAGE_BEFORE} if (defined $self->{MESSAGE_BEFORE}); |
$result .= $self->{MESSAGE_BEFORE} if (defined $self->{MESSAGE_BEFORE}); |
|
|
Line 1139 sub render {
|
Line 1206 sub render {
|
return "<font color='red' size='+1'>Something has gone wrong with the map selection feature. Please contact your administrator.</font>"; |
return "<font color='red' size='+1'>Something has gone wrong with the map selection feature. Please contact your administrator.</font>"; |
} |
} |
|
|
my $iterator = $navmap->getIterator(undef, undef, undef, 1, 0); |
my $iterator = $navmap->getIterator(undef, undef, undef, 0, 0); |
my $depth = 1; |
|
$iterator->next(); # discard first BEGIN_MAP |
|
my $curRes = $iterator->next(); |
|
my $i; |
|
my $padding = " "; |
|
my $isChoosable = 0; |
|
my $filterFunc = $self->{FILTER_FUNC}; |
my $filterFunc = $self->{FILTER_FUNC}; |
my $choiceFunc = $self->{CHOICE_FUNC}; |
my $choiceFunc = $self->{CHOICE_FUNC}; |
|
|
$result .= "<table border='0'>\n"; |
# Create the composite function that renders the column on the nav map |
|
my $renderColFunc = sub { |
while ($depth > 0) { |
my ($resource, $part, $params) = @_; |
if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } |
|
if ($curRes == $iterator->END_MAP()) { $depth--; } |
if (!&$choiceFunc($resource)) { |
|
return '<td> </td>'; |
if (ref($curRes) && &$filterFunc($curRes)) { |
} else { |
$result .= "<tr><td>"; |
my $col = "<td><input type='radio' name='${var}.forminput' "; |
|
if ($vals->{$resource->{ID}}) { |
if (&$choiceFunc($curRes)) { |
$col .= "checked "; |
if (!$curVal) { |
|
# Set this to the first one if they have no previous |
|
# selection. |
|
$curVal = $curRes->{ID}; |
|
} |
|
|
|
$isChoosable = 1; |
|
$result .= "<input type='radio' name='${var}.forminput'"; |
|
if ($curRes->{ID} eq $curVal) { |
|
$result .= " checked"; |
|
} |
|
$result .= ' value="' . $curRes->{ID} . '" />'; |
|
} |
|
|
|
$result .= "</td><td>"; |
|
|
|
for ($i = 0; $i < $depth; $i++) { |
|
$result .= $padding; |
|
} |
} |
|
$col .= "value='" . $resource->{ID} . "' /></td>"; |
#$result .= "<img border='0' src='/adm/lonIcons/navmap.folder.open.gif'>"; |
return $col; |
$result .= $curRes->compTitle . "</td></tr>\n"; |
|
} |
} |
|
}; |
|
|
$curRes = $iterator->next(); |
$result .= |
} |
&Apache::lonnavmaps::render( { "iterator" => $iterator, |
|
'cols' => [$renderColFunc, |
$result .= "</table>\n"; |
Apache::lonnavmaps::resource()], |
|
'showParts' => 0, |
|
'queryString' => $wizard->queryStringVars() . '&folderManip=1', |
|
'url' => '/adm/wizard'} ); |
|
|
$navmap->untieHashes(); |
$navmap->untieHashes(); |
|
|
if (!$isChoosable) { |
|
# FIXME: Check all the wiz vars for this. |
|
$result .= "<p><font color='#ff0000'>There are no valid resources to select in this course.</font> The entire course will be selected by default (as if 1you selected "Set for Whole Course" on the previous screen).</p>"; |
|
$result .= "<input type='hidden' name='${var}.forminput' value='0.0' />\n"; |
|
} |
|
|
|
$result .= "<p>(Note: I need to add the icons in.)</p>"; |
|
$result .= $self->{MESSAGE_AFTER} if (defined $self->{MESSAGE_AFTER}); |
$result .= $self->{MESSAGE_AFTER} if (defined $self->{MESSAGE_AFTER}); |
|
|
return $result; |
return $result; |