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

Hello everyone,

I've looked everywhere for an answer, but I can't seem to find a description of a nice way to do this.

I'm writing a perl / cgi script that has a number of views. Let's say you can move through web pages in the following sequence:

View 1 -> View 2 -> View 3 (password-protected)

View 3 is password-protected. If you're not logged in when you request it, the script redirects you to the login screen. So basically your path looks like this:

View 1 -> View 2 -> Login -> View 3

Now, how do you come back from View 3 to View 1. Would I store this sequence in a hidden variable and update it for each view? It's easy to remember just the last view, but it doesn't work beyond 2 screens. If I'm in View 3, I remember that the last view was View 2, but I don't know what comes before View 1.

Does anyone know of a nice and clean way to program this? Any examples of code? The code I've got is a mess - I would hate to maintain it, so I need to find a cleaner solution.

Thank you,
Alex

Replies are listed 'Best First'.
Re: Storing sequence of cgi states
by charnos (Friar) on Oct 08, 2002 at 14:57 UTC
    CGI::Application seems to be like what you're looking for. This won't solve all of your problems, but the module is certainly a means to accomplish having multiple forms in one application. From the POD: "The guiding philosophy behind CGI::Application is that a web-based application can be organized into a specific set of "Run-Modes." Each Run-Mode is roughly analogous to a single screen (a form, some output, etc.). All the Run-Modes are managed by a single "Application Module" which is a Perl module. In your web server's document space there is an "Instance Script" which is called by the web server as a CGI (or an Apache::Registry script if you're using Apache + mod_perl). ". I'm thinking (having not used CGI::Application before) that this is similar to what you're trying to accomplish, and should be a valuable tool to build your app.

    All in all, this method may require more code than you're willing (or is necessary) to write for the project, without knowing more about the project, I can't say. Though for storing more than one state, I'd say that using this module would be much more coherent and reusable than using hidden inputs.
Re: Storing sequence of cgi states
by fireartist (Chaplain) on Oct 08, 2002 at 16:39 UTC
    You could use an array to store a list of all page views.
    Store it in the url (or as a hidden field) as a single param,
    ?history=view1,view2,view3 Then when the script is invoked, turn this param into an array.
    my ($q, %param, @history); use CGI; $q = new CGI; %param = $q->Vars; if (defined $param{history}) { @history = split(',', $param{history}); }
    Now you have a @history array which lists all previous 'pages' viewed.
    To add another onto the end (which you must do to keep the list updated for every view.) you could do something like,
    push @history, $param{view_page} And make sure you add the updated history list back to the url (or hidden field) for every link in the new page you display.
    my $history = join(',', @history); my $page1url = $q->url(-absolute=>1) . '?history=' . $history&view_page=view1; my $page2url = $q->url(-absolute=>1) . '?history=' . $history&view_page=view2;

    The examples above are just to explain the theory. Listen to the above advice by charnos and rdfield regarding wider application design, and at the very least make sure you TAINT check the input before doing anything with it.

    You'll also have to think about things such as,
    if the user follows a link you create named 'back' from View3 to View2, you'll have a list like this view1,view2,view3.
    Do you then add view2 to the end, giving you view1,view2,view3,view2?
    or do you pop view3 off, giving you view1,view2?

    This kind of stuff can get messy real fast, so plan well!
    If you're going to use this to force users along a certain route, that's ok (?!), but if you want to provide back links, here's an article by Abigail which explains why back links with HTML and CGI generally don't work, you'll find it here, http://www.foad.org/~abigail/HTML/Misc/back_button.html
Re: Storing sequence of cgi states
by khudgins (Sexton) on Oct 08, 2002 at 18:30 UTC
    Are you tracking sessions? If so, you can store your data from each form in your session storage (Cookies, database, flat-file, etc).

    Let's say you're using cookies to toss the browser a session ID. Then, you're storing all session data in a file. (That's the way our web store handles shopping carts here at work.)

    You can then put data in the cart like this:

    open DATAFILE ">>$datadir/$sessid"; foreach $thing (keys %formdata) { print DATAFILE "$thing: $formdata{$thing}\n";

    Then, you can suck that file in at the beginning of your CGI script and have the data in memory. A quick hack would be to have a hidden input tag on each form set to true. So, if someone needs to go back to form 1, they can click on the form1 link and you do this in your script:

    if ($formToShow eq 'form1') { if ($sessionData{form1} eq 'True') { #Populate form fields here } else { #display unpopulated form here }

    I hope that made sense.
Re: Storing sequence of cgi states
by relax99 (Monk) on Oct 08, 2002 at 14:59 UTC
    Correction (second paragraph from the bottom): "If I'm in View 3, I remember that the last view was View 2, but I don't know what comes before View 2
      Sounds like you need a session keyed stack. Have a look at the various Apache:: modules to store session based info, and you'll need some kind of shared caching structure (eg IPC::SharedCache). You might want to have a look at mod_perl too: the various Auth modules work really well for "click-path" security, ie remembering where you wanted to go before the login screen popped up.

      rdfield

        Thank you for your advice. I looked at IPC::SharedCache, but why would I need something like that? It sounds like what this module is doing is just speeding up template loading, which would be nice and I had been definitely thinking about implementing some kinda caching in my application, but it doesn't really solve my original problem.