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

Hi

I've three methods for using Captcha::reCAPTCHA (v 0.92) presented below - one with CGI only and two with CGI::FormBuilder and TT2.

Method 0 works well and not only displays the HTML markup it also provides for recaptcha.net's error messaging capability.

In both the CGI::FB/TT2 methods I attempt to use the invalid field to show an error message but that doesn't work in either method.

In Method 1, the HTML markup has been manually inserted into the template and the error management in the script works but I cannot achieve any indication of the error message in the output. The HTML markup is static and I will never present an error parameter to the recaptcha server.

In Method 2, I attempt to use the Captcha::reCAPTCHA get_html method and assign this to a field value - which I then attempt to print out in the form but all I get is ARRAY(0x865c8dc). I've attempted to step through this array in the template but no joy. The HTML markup is only printed when I use Data::Dumper on the variable containing the return value of the Captcha::reCAPTCHA get_html method.

I would like Method 2 to work. The get_html method  Returns a string containing the HTML that should be used to display the captcha.. It uses HTML::Tiny to construct the string and I have v1.03 installed.

Using either Data::Dumper($crc_html); or print $crc_html; in Method 2 outputs the HTML markup and so creates the captcha. I am left wondering why the assignment to the $crc_field{'values'} key returns an apparently empty array in the template and how to get the results of the get_html method into a template. Any suggestions gratefully received.

Code is attached below. Any code review also welcome!

Regards Lesley

Method 0

Uses CGI and the Captcha::reCAPTCHA get_html method. (The example supplied with Captcha::reCAPTCHA modified for use with the CGI module.)

#!/usr/bin/perl # Simple CGI Captcha use strict; use warnings; use Captcha::reCAPTCHA; use CGI; use lib "/path/to/keys"; use Keys qw(PRIVATE_KEY PUBLIC_KEY); $| = 1; my $q = CGI->new; my $c = Captcha::reCAPTCHA->new; my $error = undef; print "Content-type: text/html\n\n"; print <<EOT; <html> <body> <form action="" method="post"> EOT # Check response if ( $q->param( 'recaptcha_response_field' ) ) { my $result = $c->check_answer( PRIVATE_KEY, $ENV{'REMOTE_ADDR'}, $q->param( 'recaptcha_challenge_field' ), $q->param( 'recaptcha_response_field' ) ); if ( $result->{is_valid} ) { print "Yes!"; } else { $error = $result->{error}; } } # Generate the form print $c->get_html( PUBLIC_KEY, $error ); print <<EOT; <br/> <input type="submit" value="submit" /> </form> </body> </html> EOT

Method 1

Uses TT2 and CGI::FormBuilder with the Captcha::reCPATCHA markup manually inserted into the template. Recognises a failed captcha but doesn't exploit the error messages available via Method 0.

Template 1

[%- INCLUDE headConst.1.0 -%] [%- PROCESS head -%] <div id="pagebody"> [%- form.start %] <div class="formelement"> <div class="cf_label">[% form.field.email.label %]</div> [% +form.field.email.field %] [%- IF form.field.email.invalid -%] <span class="cf_err">*Please correct this entry.</span> [%- END %] </div><!-- formelement (email) --> <div class="formelement"> <script type="text/javascript" src="http://api.recaptcha.net +/challenge?k=6LeiTAMAAAAAAGCJ606o9RHGHnuj2nBFes-OF0Az"> </script> <div class="cf_label"> <noscript> <iframe src="http://api.recaptcha.net/noscript?k=6LeiTAMAA +AAAAGCJ606o9RHGHnuj2nBFes-OF0Az" height="300" width="500" frameborder +="0"></iframe><br> <textarea name="recaptcha_challenge_field" rows="3" cols=" +40"> </textarea> <input type="hidden" name="recaptcha_response_field" value +="manual_challenge"> </noscript> </div> [%- IF form.field.crc_field.invalid -%] <span class="cf_err">*Please retry.</span> [%- END %] </div> <div class="formelement"> <div class="cf_label">[% form.field.send.label %] </div> [% +form.field.send.field %] </div><!-- formelement (submit) --> [% form.end %] </div><!-- pagebody --> [% PROCESS foot %]

Script 1

#!/usr/bin/perl -T use strict; use warnings; use CGI; use CGI::Carp qw(fatalsToBrowser); use CGI::FormBuilder; use CGI::FormBuilder::Template::TT2; use Email::Valid qw(address); use Captcha::reCAPTCHA; use Data::Dumper; use lib "/path/to/keys"; use Keys qw(PRIVATE_KEY PUBLIC_KEY); $CGI::POST_MAX = 1048576; # max 1MB allowed $CGI::DISABLE_UPLOADS = 1; # disable file uploads delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safer $ENV{PATH} = '/usr/bin:/usr/local/bin:usr/lib'; ## set the global data my $crc = Captcha::reCAPTCHA->new; my $crc_error = undef; my $formdata ; my %valid = ( ## initially all valid 'email' => 0, 'crc' => 0, ); my $addr; my %email = ( ## the email field name => 'email', id => 'email', label => 'Your email address : ', values => $formdata->{email}, tabindex => 2, size => 50, maxlength =>100, invalid => $valid{'email'}, ); my %crc_field = ( type => 'hidden', name => 'crc', id => 'crc', invalid => $valid{'crc'}, values => sprintf("%s",$crc->get_html(PUBLIC_KEY, $crc_error)), ); my %send = ( ## the submit button name => 'send', id => 'send', type => 'submit', value => 'Send', label => 'Hit this button :', tabindex => 5, ); my @formfields = ('email', 'crc_field', 'send', ); ## the elements in +the form my $form = CGI::FormBuilder->new( template => { ## the templ +ate being used type=> 'TT2', template => 'method1.tt', variable => 'form', engine => { INCLUDE_PATH => './templates', RELATIVE => '1', } }, id => 'form', fields => \@formfields , ## declares +where the formfields can be found method => 'post', ## the HTML +form method action => $ENV{SCRIPT_NAME}, ## ensure th +e action is set properly sticky => 1, #debug => 2, ## sets the + debug level int[0,3] ? ); sub setfields { $form->field( %email ); $form->field( %crc_field ); $form->field( %send ); } sub chk_email { # uses Email::Valid to check $addr is a valid email address my $addr = shift; my $emv = Email::Valid->new(); $emv->tldcheck(1); $emv->mxcheck(1); $addr = $emv->address(-address => $addr) if $addr; return $addr; } sub chk_form { # check the form data server side # returns true if it is ok my $ok = 0; ## not ok yet $formdata = $form->field; ## retrieve the form data my $addr = &chk_email($formdata->{'email'}) ; ## check the +email address if (defined $addr) { $valid{'email'} = 1; $ok = 1; } else { $valid{'email'} = 0; } } ## program starts # &setfields(); # set the form fields if ($form->submitted ) { my $ok = &chk_form(); ## check the form contents # Now check reCAPTCHA response my $crc_result; my $crc_response; my $crc_challenge; ## captcha data extracted from the form via cgi parameters $crc_response = $form->cgi_param( 'recaptcha_response_field' ) ; $crc_challenge = $form->cgi_param( 'recaptcha_challenge_field' ) ; $crc_result = $crc->check_answer( PRIVATE_KEY, $ENV{'REMOTE_ADDR'}, $crc_challenge, $crc_response, ); ## so now i have the result which may or may not contain the error ## $crc_result->{'is-valid'} will be either 0 or 1. if ( ( $crc_result->{is_valid} ) && $ok ) { ## passed all the valid +ation ##&cf_print(); print "Content-type: text/html\n\nall ok\n<br />"; } else { ## failed validation - there's an error if ($valid{'email'} == 0 ) { $email{'invalid'} = 1; } else { $email{'invalid'} = 0; } if ( (!( $crc_result->{is_valid} )) ) { $crc_field{'values'} = $crc_result->{error}; $crc_field{'invalid'} = 1; } &setfields(); # set the form fields print $form->render(header => 1); ## re render the form and deal w +ith the validity of the fields in the template print "looked baad\n"; print "xxx".$crc_response."xxx\n<br />"; print "xxx".$crc_challenge."xxx\n<br />"; print Dumper($crc_result); print "ok : ".$ok."<br />\n"; } } else { print $form->render(header => 1); }

Method 2

Uses TT2 and CGI::FormBuilder with the Captcha::reCPATCHA get_html method used to retrieve the HTML markup. Attempts to display the retrieved HTML fail but the HTML markup is printed out when Data::Dumper is used or the $crc_html variable is printed out directly.

Template 2

[%- INCLUDE headConst.1.0 -%] [%- PROCESS head -%] <div id="pagebody"> [%- form.start %] <div class="formelement"> <div class="cf_label">[% form.field.email.label %]</div> [% +form.field.email.field %] [%- IF form.field.email.invalid -%] <span class="cf_err">*Please correct this entry.</span> [%- END %] </div><!-- formelement (email) --> <div class="formelement"> [%- IF form.field.crc_field.values -%] [% form.field.crc_field.values -%] [%- END -%] [%- FOREACH val IN form.field.crc_field.values -%] value = [% val %] [%- END -%] [%- IF form.field.crc_field.invalid -%] <span class="cf_err">*Please retry.</span> [%- END %] </div><!-- formelement (captcha) --> <div class="formelement"> <div class="cf_label">[% form.field.send.label %] </div> [% +form.field.send.field %] </div><!-- formelement (submit) --> [% form.end %] </div><!-- pagebody --> [% PROCESS foot %]

Script 2

#!/usr/bin/perl -T use strict; use warnings; use CGI; use CGI::Carp qw(fatalsToBrowser); use CGI::FormBuilder; use CGI::FormBuilder::Template::TT2; use Email::Valid qw(address); use Captcha::reCAPTCHA; use lib "/path/to/keys"; use Keys qw(PRIVATE_KEY PUBLIC_KEY); use Data::Dumper; $CGI::POST_MAX = 1048576; # max 1MB allowed $CGI::DISABLE_UPLOADS = 1; # disable file uploads delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safer $ENV{PATH} = '/usr/bin:/usr/local/bin:usr/lib'; ## set the global data my $crc = Captcha::reCAPTCHA->new; my $crc_error = undef; my $crc_html = $crc->get_html(PUBLIC_KEY, $crc_error); my $formdata ; my %valid = ( ## initially all valid 'email' => 0, 'crc' => 0, ); my $addr; my %email = ( ## the email field name => 'email', id => 'email', label => 'Your email address : ', values => $formdata->{email}, tabindex => 2, size => 50, maxlength =>100, invalid => $valid{'email'}, ); my %crc_field = ( type => 'hidden', name => 'crc', id => 'crc', invalid => $valid{'crc'}, values => $crc_html, ); my %send = ( ## the submit button name => 'send', id => 'send', type => 'submit', value => 'Send', label => 'Hit this button :', tabindex => 5, ); my @formfields = ('email', 'crc_field', 'send', ); ## the elements in +the form my $form = CGI::FormBuilder->new( template => { ## the templ +ate being used type=> 'TT2', template => 'method2.tt', variable => 'form', engine => { INCLUDE_PATH => './templates', RELATIVE => '1', } }, id => 'form', fields => \@formfields , ## declares +where the formfields can be found method => 'post', ## the HTML +form method action => $ENV{SCRIPT_NAME}, ## ensure th +e action is set properly sticky => 1, #debug => 2, ## sets the + debug level int[0,3] ? ); sub setfields { $form->field( %email ); $form->field( %crc_field ); $form->field( %send ); } sub chk_email { # uses Email::Valid to check $addr is a valid email address my $addr = shift; my $emv = Email::Valid->new(); $emv->tldcheck(1); $emv->mxcheck(1); $addr = $emv->address(-address => $addr) if $addr; return $addr; } sub chk_form { # check the form data server side # returns true if it is ok my $ok = 0; ## not ok yet $formdata = $form->field; ## retrieve the form data my $addr = &chk_email($formdata->{'email'}) ; ## check the +email address if (defined $addr) { $valid{'email'} = 1; $ok = 1; } else { $valid{'email'} = 0; } } ## program starts # &setfields(); # set the form fields if ($form->submitted ) { my $ok = &chk_form(); ## check the form contents # Now check reCAPTCHA response my $crc_result; my $crc_response; my $crc_challenge; ## captcha data extracted from the form via cgi parameters $crc_response = $form->cgi_param( 'recaptcha_response_field' ) ; $crc_challenge = $form->cgi_param( 'recaptcha_challenge_field' ) ; $crc_result = $crc->check_answer( PRIVATE_KEY, $ENV{'REMOTE_ADDR'}, $crc_challenge, $crc_response, ); ); ## so now i have the result which may or may not contain the error ## $crc_result->{'is-valid'} will be either 0 or 1. if ( ( $crc_result->{is_valid} ) && $ok ) { ## passed all the valid +ation ##&cf_print(); print "Content-type: text/html\n\nall ok\n<br />"; } else { ## failed validation - there's an error $crc_html = $crc->get_html(PUBLIC_KEY, $crc_error); if ($valid{'email'} == 0 ) { $email{'invalid'} = 1; } else { $email{'invalid'} = 0; } if ( (!( $crc_result->{is_valid} )) ) { $crc_field{'values'} = $crc_result->{error}; $crc_field{'invalid'} = 1; } else { $crc_field{'invalid'} = 0; } &setfields(); # set the form fields print $form->render(header => 1); ## re render the form and deal w +ith the validity of the fields in the template print "looked baad\n"; print "xxx".$crc_response."xxx\n<br />"; print "xxx".$crc_challenge."xxx\n<br />"; print Dumper($crc_result); print Dumper($crc_html); print "ok : ".$ok."<br />\n"; } } else { print $form->render(header => 1); print Dumper($crc_html); }

Replies are listed 'Best First'.
Re: Captcha::reCAPTCHA with CGI::FormBuilder and TT2
by LesleyB (Friar) on Nov 19, 2008 at 11:10 UTC

    Hi

    This is an extension on this theme so I am adding this query here in the hope the thread will bump in some way and someone might be able to offer some advice.

    I have this CGI::FormBuider object instantiation

    my $form = CGI::FormBuilder->new( template => { ## the templ +ate being used type=> 'TT2', template => 'method1.tt', variable => 'form', engine => { INCLUDE_PATH => './templates', RELATIVE => '1', }, data => { twinkle => 'this is a test', recaptcha => $recaptcha, }, }, id => 'form', fields => \@formfields , ## declares +where the formfields can be found method => 'post', ## the HTML +form method action => $ENV{SCRIPT_NAME}, ## ensure th +e action is set properly sticky => 1, #debug => 2, ## sets the + debug level int[0,3] ? );

    Specifically please note the data element in the template hash.

    I set my $recaptcha = $crc->get_html(PUBIC_KEY);, $crc being my Captcha::reCAPTCHA object, and have verified that $recaptcha does hold the expected markup.

    When I write [% twinkle %] in the template I get the expected result - i.e. the string is printed out.

    When I try [% recaptcha %] I get nothing out. If I print $recaptcha; after printing the form headers, I do get the recaptcha showing.

    I am trying to glean why from http://search.cpan.org/grep?cpanid=NWIGER&release=CGI-FormBuilder-3.0501&string=data&i=1&n=1&C=0#lib/CGI/FormBuilder/Template/TT2.pm but I wondered if anyone has similar experience with the problem of passing non-form variable data to a form using CGI::FormBuilder and TT2 and let me know if I am on a hiding to nothing here.

    Regards and thanks in advance

    Lesley

    Update: I also added teststr => $test, to the data hash and then added [% teststr %] to the template and achieved no output with this construct either.