nysus has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

I have a long CGI form that I'd like to break into 3 forms, all generated and processed with a single .cgi program using CGI.pm. One of the advantages of using CGI.pm is that it allows you to save the state of the form from one submission to the next. But I'm not sure I exactly grasp how it works in practice.

In my 3 part form, I am still finding it necessary to use hidden text fields in Form #2 to carry elements from Form #1 and get them into Form #3. Isn't CGI.pm supposed to help you do away with hidden fields by carrying data from previous form element forward? How do I do this? Thanks.

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar";
$nysus = $PM . $MCF;

Replies are listed 'Best First'.
Re: Maintaining state with CGI.pm
by Masem (Monsignor) on May 31, 2001 at 22:12 UTC
    You probably want to use is the self_url of CGI.pm; this returns a URL with all the state information that you need, so that you don't need to keep filling in hidden fields for old data. Just pass this as the ACTION method for your FORM, and that data is carried across. Note that you might have to play with adding and deleting values from the CGI instance variable so that they don't show up in this URL (for example, the page of 3 that you are on).

    However, while this will work, as the amount of data entered increases, it is probably better to save the state of the data in files or databases on your end, using a unique session id that you then pass to further URLs, and upon completion of the last page, actually transferring all that data from files or databased into the final storage location that it will be stored in. This will be less prone to security faults as well, since only one state item (the session id) is passed.


    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
      This post reminds me of a problem I am currently experiencing. . . If the state of the data is stored in a database, for example, and we pass around a session ID, how do we keep our database from becoming cluttered? How and when do we clean up the data left behind by no-longer used sessions? I imagine schedule a task to delete unused sessions, but by what criteria? Do we update the session timestamp every time a user performs and action, and delete only those sessions that haven't been touched in, for example, 30 minutes?

      Just curious ;) Thanks!

      MrCromeDome

        You pretty much have the right idea. What you need to do if you go this route is have a sample selection of your users estimate the time it took to fill out the forms, and take a reasonable average and triple it. So if took the average user 15 minutes to do it, don't delete the entry for at least 45 minutes. Make sure to test this with remote users from a variety of connection types, and with users that have not filled out that form before, so that you get a good average. Of course, this time also could be a factor as determined by how many hits you get; if you expect that only one person a day will fill out a form, you probably can clear the database on a weekly basis. If, on the other hand, you get 100 people a minute filling the form, then yes, you want to minimize the time between database clearing.


        Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
        Another method of cleaning out old entries is to do so every time you enter a new entry. (I use this a lot with shopping carts and the like.) Make sure every row in the table has a timestamp for when it was created and modified, then have a single DELETE statement remove all entries before a cutoff date. (half a day, 1 week, etc.)

        -franknmonk
      Thanks.

      I toyed with the database idea last week but was missing the crucial piece of the puzzle you mentioned: having a unique session ID.

      I think I'm going to need to purchase another O'Reilly book on this topic and get much more familiar with this topic.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar";
      $nysus = $PM . $MCF;

        As merlyn has pointed out in the past, you can use the following code to generate a unique session_id. If you are going to use it for generating a file to save the CGI object in, be sure to use Taint checks.
        use MD5; sub generate_id { return substr(MD5->hexhash(time(). {}. rand(). $$. 'blah'), 0, 16) +; }


        You may find this recent discussion relevant.

        -Lee

        "To be civilized is to deny one's nature."
(jeffa) Re: Maintaining state with CGI.pm
by jeffa (Bishop) on May 31, 2001 at 22:21 UTC
    Although it is aimed at mod_perl and not CGI.pm in particular, Writing Apache Modules with Perl and C offers a complete chapter about maintaining state. The chapter includes techniques using hidden fields, cookies, and database methods, as well as techniques for checksum comparisons and encryption. Good stuff. :)

    Jeff

    R-R-R--R-R-R--R-R-R--R-R-R--R-R-R--
    L-L--L-L--L-L--L-L--L-L--L-L--L-L--
    
(zdog) Re: Maintaining state with CGI.pm
by zdog (Priest) on May 31, 2001 at 22:05 UTC
    CGI.pm offers a good interface for dealing with cookies. You should use those to store information on the users' computers and easily retrieve it for the script. The CGI documentation should tell you how to use those.

    Zenon Zabinski | zdog | zdog7@hotmail.com

      Is this the best way to handle this? What if cookies are turned off?

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar";
      $nysus = $PM . $MCF;

        Or you can pass the parameters from the previous form to the next form. You can set the parameters by using the param() routine similarly to this:

        $q->param(-name=>'veggie',-value=>'tomato');

        Update: My bad. You can pass the parameters to the next form by using the hidden input method. (See reply.)

        Zenon Zabinski | zdog | zdog7@hotmail.com

Re: Maintaining state with CGI.pm
by princepawn (Parson) on May 31, 2001 at 23:17 UTC