Annotation of loncom/interface/lonhelper.pm, revision 1.4

1.1       bowersj2    1: # The LearningOnline Network with CAPA
                      2: # .helper XML handler to implement the LON-CAPA helper
                      3: #
1.3       bowersj2    4: # $Id: lonhelper.pm,v 1.2 2003/03/21 21:34:56 bowersj2 Exp $
1.1       bowersj2    5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
                     28: # (Page Handler
                     29: #
                     30: # (.helper handler
                     31: #
                     32: 
1.3       bowersj2   33: =pod
                     34: 
                     35: =head1 lonhelper - HTML Helper framework for LON-CAPA
                     36: 
                     37: Helpers, often known as "wizards", are well-established UI widgets that users
                     38: feel comfortable with. It can take a complicated multidimensional problem the
                     39: user has and turn it into a series of bite-sized one-dimensional questions.
                     40: 
                     41: For developers, helpers provide an easy way to bundle little bits of functionality
                     42: for the user, without having to write the tedious state-maintenence code.
                     43: 
                     44: Helpers are defined as XML documents, placed in the /home/httpd/html/adm/helpers 
                     45: directory and having the .helper file extension. For examples, see that directory.
                     46: 
                     47: All classes are in the Apache::lonhelper namespace.
                     48: 
                     49: =head2 lonxml
                     50: 
                     51: The helper uses the lonxml XML parsing support. The following capabilities
                     52: are directly imported from lonxml:
                     53: 
                     54: =over 4
                     55: 
                     56: =item * <startouttext> and <endouttext>: These tags may be used, as in problems,
                     57:         to directly output text to the user.
                     58: 
                     59: =back
                     60: 
                     61: =head2 lonhelper XML file format
                     62: 
                     63: A helper consists of a top-level <helper> tag which contains a series of states.
                     64: Each state contains one or more state elements, which are what the user sees, like
                     65: messages, resource selections, or date queries.
                     66: 
                     67: The helper tag is required to have one attribute, "title", which is the name
                     68: of the helper itself, such as "Parameter helper". 
                     69: 
                     70: =head2 State tags
                     71: 
                     72: State tags are required to have an attribute "name", which is the symbolic
                     73: name of the state and will not be directly seen by the user. The wizard is
                     74: required to have one state named "START", which is the state the wizard
                     75: will start with. by convention, this state should clearly describe what
                     76: the helper will do for the user, and may also include the first information
                     77: entry the user needs to do for the helper.
                     78: 
                     79: State tags are also required to have an attribute "title", which is the
                     80: human name of the state, and will be displayed as the header on top of 
                     81: the screen for the user.
                     82: 
                     83: =head2 Example Helper Skeleton
                     84: 
                     85: An example of the tags so far:
                     86: 
                     87:  <helper title="Example Helper">
                     88:    <state name="START" title="Demonstrating the Example Helper">
                     89:      <!-- notice this is the START state the wizard requires -->
                     90:      </state>
                     91:    <state name="GET_NAME" title="Enter Student Name">
                     92:      </state>
                     93:    </helper>
                     94: 
                     95: Of course this does nothing. In order for the wizard to do something, it is
                     96: necessary to put actual elements into the wizard. Documentation for each
                     97: of these elements follows.
                     98: 
                     99: =cut
                    100: 
1.1       bowersj2  101: package Apache::lonhelper;
1.2       bowersj2  102: use Apache::Constants qw(:common);
                    103: use Apache::File;
1.3       bowersj2  104: use Apache::lonxml;
1.2       bowersj2  105: 
                    106: BEGIN {
                    107:     &Apache::lonxml::register('Apache::lonhelper', 
1.4     ! bowersj2  108:                               ('helper', 'state'));
1.2       bowersj2  109: }
                    110: 
1.3       bowersj2  111: # Since all wizards are only three levels deep (wizard tag, state tag, 
                    112: # substate type), it's easier and more readble to explicitly track 
                    113: # those three things directly, rather then futz with the tag stack 
                    114: # every time.
                    115: my $helper;
                    116: my $state;
                    117: my $substate;
1.4     ! bowersj2  118: # To collect parameters, the contents of the subtags are collected
        !           119: # into this paramHash, then passed to the element object when the 
        !           120: # end of the element tag is located.
        !           121: my $paramHash; 
1.2       bowersj2  122: 
                    123: sub handler {
1.3       bowersj2  124:     my $r = shift;
1.2       bowersj2  125:     $ENV{'request.uri'} = $r->uri();
                    126:     my $filename = '/home/httpd/html' . $r->uri();
                    127:     my $fh = Apache::File->new($filename);
                    128:     my $file;
1.3       bowersj2  129:     read $fh, $file, 100000000;
                    130: 
                    131:     Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});
                    132: 
                    133:     # Send header, don't cache this page
                    134:     if ($r->header_only) {
                    135:         if ($ENV{'browser.mathml'}) {
                    136:             $r->content_type('text/xml');
                    137:         } else {
                    138:             $r->content_type('text/html');
                    139:         }
                    140:         $r->send_http_header;
                    141:         return OK;
                    142:     }
                    143:     if ($ENV{'browser.mathml'}) {
                    144:         $r->content_type('text/xml');
                    145:     } else {
                    146:         $r->content_type('text/html');
                    147:     }
                    148:     $r->send_http_header;
                    149:     $r->rflush();
1.2       bowersj2  150: 
1.3       bowersj2  151:     # Discard result, we just want the objects that get created by the
                    152:     # xml parsing
                    153:     &Apache::lonxml::xmlparse($r, 'helper', $file);
1.2       bowersj2  154: 
1.3       bowersj2  155:     $r->print($helper->display());
1.2       bowersj2  156:     return OK;
                    157: }
                    158: 
                    159: sub start_helper {
                    160:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    161: 
                    162:     if ($target ne 'helper') {
                    163:         return '';
                    164:     }
                    165:     
1.3       bowersj2  166:     $helper = Apache::lonhelper::helper->new($token->[2]{'title'});
1.4     ! bowersj2  167:     return '';
1.2       bowersj2  168: }
                    169: 
                    170: sub end_helper {
                    171:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    172:     
1.3       bowersj2  173:     if ($target ne 'helper') {
                    174:         return '';
                    175:     }
                    176:     
1.4     ! bowersj2  177:     return '';
1.2       bowersj2  178: }
1.1       bowersj2  179: 
1.3       bowersj2  180: sub start_state {
                    181:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    182: 
                    183:     if ($target ne 'helper') {
                    184:         return '';
                    185:     }
                    186: 
                    187:     $state = Apache::lonhelper::state->new($token->[2]{'name'},
                    188:                                            $token->[2]{'title'});
                    189:     return '';
                    190: }
                    191: 
                    192: # don't need this, so ignore it
                    193: sub end_state {
                    194:     return '';
                    195: }
                    196: 
1.1       bowersj2  197: 1;
                    198: 
1.3       bowersj2  199: package Apache::lonhelper::helper;
                    200: 
                    201: use Digest::MD5 qw(md5_hex);
                    202: use HTML::Entities;
                    203: use Apache::loncommon;
                    204: use Apache::File;
                    205: 
                    206: sub new {
                    207:     my $proto = shift;
                    208:     my $class = ref($proto) || $proto;
                    209:     my $self = {};
                    210: 
                    211:     $self->{TITLE} = shift;
                    212:     
                    213:     # If there is a state from the previous form, use that. If there is no
                    214:     # state, use the start state parameter.
                    215:     if (defined $ENV{"form.CURRENT_STATE"})
                    216:     {
                    217: 	$self->{STATE} = $ENV{"form.CURRENT_STATE"};
                    218:     }
                    219:     else
                    220:     {
                    221: 	$self->{STATE} = "START";
                    222:     }
                    223: 
                    224:     $self->{TOKEN} = $ENV{'form.TOKEN'};
                    225:     # If a token was passed, we load that in. Otherwise, we need to create a 
                    226:     # new storage file
                    227:     # Tried to use standard Tie'd hashes, but you can't seem to take a 
                    228:     # reference to a tied hash and write to it. I'd call that a wart.
                    229:     if ($self->{TOKEN}) {
                    230:         # Validate the token before trusting it
                    231:         if ($self->{TOKEN} !~ /^[a-f0-9]{32}$/) {
                    232:             # Not legit. Return nothing and let all hell break loose.
                    233:             # User shouldn't be doing that!
                    234:             return undef;
                    235:         }
                    236: 
                    237:         # Get the hash.
                    238:         $self->{FILENAME} = $Apache::lonnet::tmpdir . md5_hex($self->{TOKEN}); # Note the token is not the literal file
                    239:         
                    240:         my $file = Apache::File->new($self->{FILENAME});
                    241:         my $contents = <$file>;
                    242:         &Apache::loncommon::get_unprocessed_cgi($contents);
                    243:         $file->close();
                    244:     } else {
                    245:         # Only valid if we're just starting.
                    246:         if ($self->{STATE} ne 'START') {
                    247:             return undef;
                    248:         }
                    249:         # Must create the storage
                    250:         $self->{TOKEN} = md5_hex($ENV{'user.name'} . $ENV{'user.domain'} .
                    251:                                  time() . rand());
                    252:         $self->{FILENAME} = $Apache::lonnet::tmpdir . md5_hex($self->{TOKEN});
                    253:     }
                    254: 
                    255:     # OK, we now have our persistent storage.
                    256: 
                    257:     if (defined $ENV{"form.RETURN_PAGE"})
                    258:     {
                    259: 	$self->{RETURN_PAGE} = $ENV{"form.RETURN_PAGE"};
                    260:     }
                    261:     else
                    262:     {
                    263: 	$self->{RETURN_PAGE} = $ENV{REFERER};
                    264:     }
                    265: 
                    266:     $self->{STATES} = {};
                    267:     $self->{DONE} = 0;
                    268: 
                    269:     bless($self, $class);
                    270:     return $self;
                    271: }
                    272: 
                    273: # Private function; returns a string to construct the hidden fields
                    274: # necessary to have the helper track state.
                    275: sub _saveVars {
                    276:     my $self = shift;
                    277:     my $result = "";
                    278:     $result .= '<input type="hidden" name="CURRENT_STATE" value="' .
                    279:         HTML::Entities::encode($self->{STATE}) . "\" />\n";
                    280:     $result .= '<input type="hidden" name="TOKEN" value="' .
                    281:         $self->{TOKEN} . "\" />\n";
                    282:     $result .= '<input type="hidden" name="RETURN_PAGE" value="' .
                    283:         HTML::Entities::encode($self->{RETURN_PAGE}) . "\" />\n";
                    284: 
                    285:     return $result;
                    286: }
                    287: 
                    288: # Private function: Create the querystring-like representation of the stored
                    289: # data to write to disk.
                    290: sub _varsInFile {
                    291:     my $self = shift;
                    292:     my @vars = ();
                    293:     for my $key (keys %{$self->{VARS}}) {
                    294:         push @vars, &Apache::lonnet::escape($key) . '=' .
                    295:             &Apache::lonnet::escape($self->{VARS}->{$key});
                    296:     }
                    297:     return join ('&', @vars);
                    298: }
                    299: 
                    300: sub changeState {
                    301:     my $self = shift;
                    302:     $self->{STATE} = shift;
                    303: }
                    304: 
                    305: sub registerState {
                    306:     my $self = shift;
                    307:     my $state = shift;
                    308: 
                    309:     my $stateName = $state->name();
                    310:     $self->{STATES}{$stateName} = $state;
                    311: }
                    312: 
                    313: # Done in four phases
                    314: # 1: Do the post processing for the previous state.
                    315: # 2: Do the preprocessing for the current state.
                    316: # 3: Check to see if state changed, if so, postprocess current and move to next.
                    317: #    Repeat until state stays stable.
                    318: # 4: Render the current state to the screen as an HTML page.
                    319: sub display {
                    320:     my $self = shift;
                    321: 
                    322:     my $result = "";
                    323: 
                    324:     # Phase 1: Post processing for state of previous screen (which is actually
                    325:     # the "current state" in terms of the helper variables), if it wasn't the 
                    326:     # beginning state.
                    327:     if ($self->{STATE} ne "START" || $ENV{"form.SUBMIT"} eq "Next ->") {
                    328: 	my $prevState = $self->{STATES}{$self->{STATE}};
                    329:             $prevState->postprocess();
                    330:     }
                    331:     
                    332:     # Note, to handle errors in a state's input that a user must correct,
                    333:     # do not transition in the postprocess, and force the user to correct
                    334:     # the error.
                    335: 
                    336:     # Phase 2: Preprocess current state
                    337:     my $startState = $self->{STATE};
                    338:     my $state = $self->{STATES}{$startState};
                    339:     
                    340:     # Error checking; it is intended that the developer will have
                    341:     # checked all paths and the user can't see this!
                    342:     if (!defined($state)) {
                    343:         $result .="Error! The state ". $startState ." is not defined.";
                    344:         return $result;
                    345:     }
                    346:     $state->preprocess();
                    347: 
                    348:     # Phase 3: While the current state is different from the previous state,
                    349:     # keep processing.
                    350:     while ( $startState ne $self->{STATE} )
                    351:     {
                    352: 	$startState = $self->{STATE};
                    353: 	$state = $self->{STATES}{$startState};
                    354: 	$state->preprocess();
                    355:     }
                    356: 
                    357:     # Phase 4: Display.
                    358:     my $stateTitle = $state->title();
                    359:     my $bodytag = &Apache::loncommon::bodytag("$self->{TITLE}",'','');
                    360: 
                    361:     $result .= <<HEADER;
                    362: <html>
                    363:     <head>
                    364:         <title>LON-CAPA Helper: $self->{TITLE}</title>
                    365:     </head>
                    366:     $bodytag
                    367: HEADER
                    368:     if (!$state->overrideForm()) { $result.="<form name='wizform' method='GET'>"; }
                    369:     $result .= <<HEADER;
                    370:         <table border="0"><tr><td>
                    371:         <h2><i>$stateTitle</i></h2>
                    372: HEADER
                    373: 
                    374:     if (!$state->overrideForm()) {
                    375:         $result .= $self->_saveVars();
                    376:     }
                    377:     $result .= $state->render() . "<p>&nbsp;</p>";
                    378: 
                    379:     if (!$state->overrideForm()) {
                    380:         $result .= '<center>';
                    381:         if ($self->{STATE} ne $self->{START_STATE}) {
                    382:             #$result .= '<input name="SUBMIT" type="submit" value="&lt;- Previous" />&nbsp;&nbsp;';
                    383:         }
                    384:         if ($self->{DONE}) {
                    385:             my $returnPage = $self->{RETURN_PAGE};
                    386:             $result .= "<a href=\"$returnPage\">End Helper</a>";
                    387:         }
                    388:         else {
                    389:             $result .= '<input name="back" type="button" ';
                    390:             $result .= 'value="&lt;- Previous" onclick="history.go(-1)" /> ';
                    391:             $result .= '<input name="SUBMIT" type="submit" value="Next -&gt;" />';
                    392:         }
                    393:         $result .= "</center>\n";
                    394:     }
                    395: 
                    396:     $result .= <<FOOTER;
                    397:               </td>
                    398:             </tr>
                    399:           </table>
                    400:         </form>
                    401:     </body>
                    402: </html>
                    403: FOOTER
                    404: 
                    405:     # Handle writing out the vars to the file
                    406:     my $file = Apache::File->new('>'.$self->{FILENAME});
                    407:     print $file $self->_varsInFile();
                    408: 
                    409:     return $result;
                    410: }
                    411: 
                    412: 1;
                    413: 
                    414: package Apache::lonhelper::state;
                    415: 
                    416: # States bundle things together and are responsible for compositing the
1.4     ! bowersj2  417: # various elements together. It is not generally necessary for users to
        !           418: # use the state object directly, so it is not perldoc'ed.
        !           419: 
        !           420: # Basically, all the states do is pass calls to the elements and aggregate
        !           421: # the results.
1.3       bowersj2  422: 
                    423: sub new {
                    424:     my $proto = shift;
                    425:     my $class = ref($proto) || $proto;
                    426:     my $self = {};
                    427: 
                    428:     $self->{NAME} = shift;
                    429:     $self->{TITLE} = shift;
                    430:     $self->{ELEMENTS} = [];
                    431: 
                    432:     bless($self, $class);
                    433: 
                    434:     $helper->registerState($self);
                    435: 
                    436:     return $self;
                    437: }
                    438: 
                    439: sub name {
                    440:     my $self = shift;
                    441:     return $self->{NAME};
                    442: }
                    443: 
                    444: sub title {
                    445:     my $self = shift;
                    446:     return $self->{TITLE};
                    447: }
                    448: 
1.4     ! bowersj2  449: sub preprocess {
        !           450:     my $self = shift;
        !           451:     for my $element (@{$self->{ELEMENTS}}) {
        !           452:         $element->preprocess();
        !           453:     }
        !           454: }
        !           455: 
        !           456: sub postprocess {
        !           457:     my $self = shift;
        !           458:     
        !           459:     for my $element (@{$self->{ELEMENTS}}) {
        !           460:         $element->postprocess();
        !           461:     }
        !           462: }
        !           463: 
        !           464: sub overrideForm {
        !           465:     return 0;
        !           466: }
        !           467: 
        !           468: sub addElement {
        !           469:     my $self = shift;
        !           470:     my $element = shift;
        !           471:     
        !           472:     push @{$self->{ELEMENTS}}, $element;
        !           473: }
        !           474: 
        !           475: sub render {
        !           476:     my $self = shift;
        !           477:     my @results = ();
        !           478: 
        !           479:     for my $element (@{$self->{ELEMENTS}}) {
        !           480:         push @results, $element->render();
        !           481:     }
        !           482:     return join("\n", @results);
        !           483: }
        !           484: 
        !           485: 1;
        !           486: 
        !           487: package Apache::lonhelper::element;
        !           488: # Support code for elements
        !           489: 
        !           490: =pod
        !           491: 
        !           492: =head2 Element Base Class
        !           493: 
        !           494: The Apache::lonhelper::element base class provides support methods for
        !           495: the elements to use, such as a multiple value processer.
        !           496: 
        !           497: B<Methods>:
        !           498: 
        !           499: =over 4
        !           500: 
        !           501: =item * process_multiple_choices(formName, varName): Process the form 
        !           502: element named "formName" and place the selected items into the helper 
        !           503: variable named varName. This is for things like checkboxes or 
        !           504: multiple-selection listboxes where the user can select more then 
        !           505: one entry. The selected entries are delimited by triple pipes in 
        !           506: the helper variables, like this:  CHOICE_1|||CHOICE_2|||CHOICE_3
        !           507: 
        !           508: =back
        !           509: 
        !           510: =cut
        !           511: 
        !           512: # Because we use the param hash, this is often a sufficent
        !           513: # constructor
        !           514: sub new {
        !           515:     my $proto = shift;
        !           516:     my $class = ref($proto) || $proto;
        !           517:     my $self = $paramHash;
        !           518:     bless($self, $class);
        !           519: 
        !           520:     $self->{PARAMS} = $paramHash;
        !           521:     $self->{STATE} = $state;
        !           522:     $state->addElement($self);
        !           523:     
        !           524:     # Ensure param hash is not reused
        !           525:     $paramHash = {};
        !           526: 
        !           527:     return $self;
        !           528: }   
        !           529: 
        !           530: sub preprocess {
        !           531:     return 1;
        !           532: }
        !           533: 
        !           534: sub postprocess {
        !           535:     return 1;
        !           536: }
        !           537: 
        !           538: sub render {
        !           539:     return '';
        !           540: }
        !           541: 
1.3       bowersj2  542: sub process_multiple_choices {
                    543:     my $self = shift;
                    544:     my $formname = shift;
                    545:     my $var = shift;
                    546: 
                    547:     my $formvalue = $ENV{'form.' . $formname};
                    548:     if ($formvalue) {
                    549:         # Must extract values from $wizard->{DATA} directly, as there
                    550:         # may be more then one.
                    551:         my @values;
                    552:         for my $formparam (split (/&/, $wizard->{DATA})) {
                    553:             my ($name, $value) = split(/=/, $formparam);
                    554:             if ($name ne $formname) {
                    555:                 next;
                    556:             }
                    557:             $value =~ tr/+/ /;
                    558:             $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
                    559:             push @values, $value;
                    560:         }
                    561:         $helper->setVar($var, join('|||', @values));
                    562:     }
                    563:     
                    564:     return;
                    565: }
                    566: 
1.4     ! bowersj2  567: 1;
        !           568: 
        !           569: package Apache::lonhelper::message;
        !           570: 
        !           571: =pod
        !           572: 
        !           573: =head2 Element: message
        !           574: 
        !           575: Message elements display the contents of their <message_text> tags, and
        !           576: transition directly to the state in the <next_state> tag. Example:
        !           577: 
        !           578:  <message>
        !           579:    <next_state>GET_NAME</next_state>
        !           580:    <message_text>This is the <b>message</b> the user will see, 
        !           581:                  <i>HTML allowed</i>.</message_text>
        !           582:    </message>
        !           583: 
        !           584: This will display the HTML message and transition to the <next_state> if
        !           585: given. The HTML will be directly inserted into the wizard, so if you don't
        !           586: want text to run together, you'll need to manually wrap the <message_text>
        !           587: in <p> tags, or whatever is appropriate for your HTML.
        !           588: 
        !           589: This is also a good template for creating your own new states, as it has
        !           590: very little code beyond the state template.
        !           591: 
        !           592: =cut
        !           593: 
        !           594: no strict;
        !           595: @ISA = ("Apache::lonhelper::element");
        !           596: use strict;
        !           597: 
        !           598: BEGIN {
        !           599:     &Apache::lonxml::register('Apache::lonhelper::message',
        !           600:                               ('message', 'next_state', 'message_text'));
1.3       bowersj2  601: }
                    602: 
1.4     ! bowersj2  603: # Don't need to override the "new" from element
        !           604: 
        !           605: # CONSTRUCTION: Construct the message element from the XML
        !           606: sub start_message {
        !           607:     return '';
1.3       bowersj2  608: }
                    609: 
1.4     ! bowersj2  610: sub end_message {
        !           611:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
        !           612: 
        !           613:     if ($target ne 'helper') {
        !           614:         return '';
        !           615:     }
        !           616:     Apache::lonhelper::message->new();
        !           617:     return '';
1.3       bowersj2  618: }
                    619: 
1.4     ! bowersj2  620: sub start_next_state {
        !           621:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
        !           622: 
        !           623:     if ($target ne 'helper') {
        !           624:         return '';
        !           625:     }
1.3       bowersj2  626:     
1.4     ! bowersj2  627:     $paramHash->{NEXT_STATE} = &Apache::lonxml::get_all_text('/next_state',
        !           628:                                                              $parser);
        !           629:     return '';
        !           630: }
        !           631: 
        !           632: sub end_next_state { return ''; }
        !           633: 
        !           634: sub start_message_text {
        !           635:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
        !           636: 
        !           637:     if ($target ne 'helper') {
        !           638:         return '';
        !           639:     }
        !           640: 
        !           641:     $paramHash->{MESSAGE_TEXT} = &Apache::lonxml::get_all_text('/message_text',
        !           642:                                                                $parser);
1.3       bowersj2  643: }
1.4     ! bowersj2  644:     
        !           645: sub end_message_text { return 1; }
1.3       bowersj2  646: 
                    647: sub render {
                    648:     my $self = shift;
                    649: 
1.4     ! bowersj2  650:     return $self->{MESSAGE_TEXT};
        !           651: }
        !           652: # If a NEXT_STATE was given, switch to it
        !           653: sub postprocess {
        !           654:     my $self = shift;
        !           655:     if (defined($self->{NEXT_STATE})) {
        !           656:         $helper->changeState($self->{NEXT_STATE});
1.3       bowersj2  657:     }
                    658: }
1.4     ! bowersj2  659: 1;
1.3       bowersj2  660: 
1.1       bowersj2  661: __END__
1.3       bowersj2  662: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>