Receiving and Validating Docbase Records

The Docbase::Input module will automatically preview input received from a form’s handler. We’ll see how that works shortly. But validating the fields of a record is the responsibility of each Docbase instance. That’s done by customizing the handler for each instance. The handler is always called submit.pl. For the ProductAnalysis docbase, the customized handler—shown in Example 6.4—is stored as /web/cgi-bin/Docbase/ProductAnalysis/submit.pl.

Example 6-4. The ProductAnalysis Version of submit.pl

#!/usr/bin/perl -w

###########################################################
# boilerplate for every docbase
###########################################################

use strict;
use Docbase::Input;
use TinyCGI;

my $tc = TinyCGI->new();                       
my $vars = $tc->readParse();                   # collect CGI args 
print $tc->printHeader;                        # start emitting page

my $app = $vars->{app};
my $di = Docbase::Input->new($app);           

foreach my $key (keys %$vars)                  # basic cleanup of input text
  {
  $vars->{$key} = $di->cleanseInput($vars->{$key});  
  }

my ($warnings,$errors) = docbase_validate();   # validate input

$di->processInput($vars,$warnings,$errors);    # process input and warnings/errors

###########################################################
# custom validation tailored for each docbase
###########################################################

sub docbase_validate
  {
  my (@warnings) = ();
  my (@errors) = ();
  my $mailpat = $di->getMailPat;
                                               # accumulate warnings

  unless ( $vars->{'contact'} =~ m#$mailpat# ) # email address wanted
    { 
    push (@warnings,"<b>contact</b> does not appear to contain an email address "); 
    }

  unless ( $vars->{'contact'} =~ m#d{3,4}# )  # phone number wanted
    { 
    push (@warnings,"<b>contact</b> does not appear to contain a phone number "); 
    }

                                               # accumulate errors

  foreach my $key (keys %$vars)    
    {
    if (! defined $vars->{$key})               # all fields must be non-empty
      { 
      push (@errors,'<b>' . $key . '</b> is empty, but required'), 
      }
    if ( $vars->{$key} =~ /<.+>/ )             # no HTML tags allowed
      { 
      push (@errors, '<b>' . $key . '</b> must not contain HTML tags'),
      }
    }
    return (@warnings,@errors);
  }

The first half of this handler is a boilerplate that’s the same for every Docbase instance. To tailor it for a new instance, you write a version of docbase_validate( ) that enforces rules specific to this instance, classifies problems as either errors or warnings, and returns these as a pair of lists.

Input Cleanup

Docbase users don’t normally compose lengthy text in the HTML form. Typically, they paste in text created by a word processor. That text sometimes includes character codes that you’ll want to convert. For example, some Macintosh programs represent a single quote as xD5. The cleanseInput( ) method of Docbase::Indexer turns that into the ASCII single quote, x27. It’s just a set of search-and-replace operations, like this:

sub cleanseInput         
  {
  my ($self, $s) = @_;
  $s =~ s/x00/ /g;
  $s =~ s/xd1/--/g;
  $s =~ s/xd2/"/g;
  $s =~ s/xd3/"/g;
  $s =~ s/xd5/'/g;
  $s =~ s/x81/Æ/g;
  return $s;
  }

You can easily extend this method to handle other conversions as the need arises.

Validation

The docbase_validate( ) function implements a crucial but often overlooked principle: handlers should parse forms completely and always report all errors and omissions. Failure to do this is endemic on the Web, and it drives users crazy. There’s nothing worse than bouncing off a server once to discover that your password is too short, then again to find out that your password includes an invalid character, then again to learn that, oh by the way, the company field that you had left blank is actually required. A form handler sees all the data you send, every time you send it. Why not look at all the data and respond intelligently and completely every time?

To do that, make two lists—one for warnings, one for errors. A warning raises an issue that the user can choose to either address or ignore; these are reported in the Optional Changes section of the preview. In this example, absence of an email address in the contact field triggers a warning. An error raises an issue that the user must deal with; these are reported under the heading Required Changes. In this example, any field that is empty, or that contains an HTML tag, triggers an error.

The docbase_validate( ) function accumulates warnings and errors, then returns the two lists. Note how the Perl idiom for doing that requires you to pass a reference to each of the sublists, rather than the sublists themselves. The latter method won’t cause an obvious error, but it will mash the two sublists into a single list, and you’ll lose the distinction between warnings and errors. Note also the two idioms for checking variables. To check a specific variable, bind a test to a slot in the hashtable. To check all variables, do that binding inside a loop that iterates over the whole hashtable.

The submit.pl script passes the form variables, and the lists of warnings and errors, to the processInput( ) method of the Docbase::Input module, shown in Example 6.5.

Example 6-5. The processInput Method

sub processInput
  {
  my ($self,$vars,$warnings,$errors) = @_;

  my $warn_count = scalar(@$warnings);  # count warnings
  my $err_count = scalar(@$errors);     # count errors

  if ( $err_count > 0 )                 # mention required changes
    {
    if ( $warn_count > 0 )              # also mention optional changes
      {
      _doWarnings($self->{app},$warn_count,$warnings);  
      }
    print "<p><b>$self->{app}: Required changes: </b>
";
    my ($i, $c);
    for $i (1..$err_count)
      { 
      print "<br>" . ++$c  . ') ' . $errors->[$i-1]; 
      }
    print "<p>Please use your browser's Go Back button ";
    print "to rewind and fix the required changes.";
    }
  elsif ( $warn_count > 0)              # mention optional changes
    {
    _doWarnings($self->{app},$warn_count,$warnings);
    print "<p>You can go back and make these optional changes. ";
    print "Or, if what you see below is OK, you can submit it.";
    print $self->_previewDoc($vars);    # preview
    }
  else    
    {
    print $self->_previewDoc($vars);    # preview
    }
  }

There are three possible outcomes. If there were errors, these are reported (along with warnings, if any) on a Required Changes page that advises the user to go back and correct them.

If there were only warnings, these are reported on an Optional Changes page that also includes a preview of the record. Here, the user has a choice: go back and fix what caused the warnings, or submit the previewed record as is.

Finally, if there were no errors or warnings, only the previewed record appears. The user can submit it or, if something needs changing, can rewind and make those changes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.36.141