# The most recent version and complete docs are available at:
# http://stein.cshl.org/WWW/software/CGI/
-$CGI::revision = '$Id: CGI.pm,v 1.165 2004/04/12 20:37:26 lstein Exp $';
-$CGI::VERSION=3.05;
+$CGI::revision = '$Id: CGI.pm,v 1.194 2005/12/06 22:12:56 lstein Exp $';
+$CGI::VERSION='3.15_01';
# HARD-CODED LOCATION FOR FILE UPLOAD TEMPORARY FILES.
# UNCOMMENT THIS ONLY IF YOU KNOW WHAT YOU'RE DOING.
# 2) CGI::private_tempfiles(1);
$PRIVATE_TEMPFILES = 0;
+ # Set this to 1 to generate automatic tab indexes
+ $TABINDEX = 0;
+
# Set this to 1 to cause files uploaded in multipart documents
# to be closed, instead of caching the file handle
# or:
# Turn on special checking for Doug MacEachern's modperl
if (exists $ENV{MOD_PERL}) {
- eval "require mod_perl";
# mod_perl handlers may run system() on scripts using CGI.pm;
# Make sure so we don't get fooled by inherited $ENV{MOD_PERL}
- if (defined $mod_perl::VERSION) {
- if ($mod_perl::VERSION >= 1.99) {
- $MOD_PERL = 2;
- require Apache::Response;
- require Apache::RequestRec;
- require Apache::RequestUtil;
- require APR::Pool;
- } else {
- $MOD_PERL = 1;
- require Apache;
- }
+ if (exists $ENV{MOD_PERL_API_VERSION} && $ENV{MOD_PERL_API_VERSION} == 2) {
+ $MOD_PERL = 2;
+ require Apache2::Response;
+ require Apache2::RequestRec;
+ require Apache2::RequestUtil;
+ require Apache2::RequestIO;
+ require APR::Pool;
+ } else {
+ $MOD_PERL = 1;
+ require Apache;
}
}
submit reset defaults radio_group popup_menu button autoEscape
scrolling_list image_button start_form end_form startform endform
start_multipart_form end_multipart_form isindex tmpFileName uploadInfo URL_ENCODED MULTIPART/],
- ':cgi'=>[qw/param upload path_info path_translated url self_url script_name cookie Dump
+ ':cgi'=>[qw/param upload path_info path_translated request_uri url self_url script_name
+ cookie Dump
raw_cookie request_method query_string Accept user_agent remote_host content_type
remote_addr referer server_name server_software server_port server_protocol virtual_port
virtual_host remote_ident auth_type http append
':all' => [qw/:html2 :html3 :netscape :form :cgi :internal :html4/]
);
+# Custom 'can' method for both autoloaded and non-autoloaded subroutines.
+# Author: Cees Hek <cees@sitesuite.com.au>
+
+sub can {
+ my($class, $method) = @_;
+
+ # See if UNIVERSAL::can finds it.
+
+ if (my $func = $class -> SUPER::can($method) ){
+ return $func;
+ }
+
+ # Try to compile the function.
+
+ eval {
+ # _compile looks at $AUTOLOAD for the function name.
+
+ local $AUTOLOAD = join "::", $class, $method;
+ &_compile;
+ };
+
+ # Now that the function is loaded (if it exists)
+ # just use UNIVERSAL::can again to do the work.
+
+ return $class -> SUPER::can($method);
+}
+
# to import symbols into caller
sub import {
my $self = shift;
if (ref($initializer[0])
&& (UNIVERSAL::isa($initializer[0],'Apache')
||
- UNIVERSAL::isa($initializer[0],'Apache::RequestRec')
+ UNIVERSAL::isa($initializer[0],'Apache2::RequestRec')
)) {
$self->r(shift @initializer);
}
$self->upload_hook(shift @initializer, shift @initializer);
}
if ($MOD_PERL) {
- $self->r(Apache->request) unless $self->r;
- my $r = $self->r;
if ($MOD_PERL == 1) {
+ $self->r(Apache->request) unless $self->r;
+ my $r = $self->r;
$r->register_cleanup(\&CGI::_reset_globals);
}
else {
# XXX: once we have the new API
# will do a real PerlOptions -SetupEnv check
+ $self->r(Apache2::RequestUtil->request) unless $self->r;
+ my $r = $self->r;
$r->subprocess_env unless exists $ENV{REQUEST_METHOD};
$r->pool->cleanup_register(\&CGI::_reset_globals);
}
# user is still holding any reference to them as well.
sub DESTROY {
my $self = shift;
- foreach my $href (values %{$self->{'.tmpfiles'}}) {
- $href->{hndl}->DESTROY if defined $href->{hndl};
- $href->{name}->DESTROY if defined $href->{name};
+ if ($OS eq 'WINDOWS') {
+ foreach my $href (values %{$self->{'.tmpfiles'}}) {
+ $href->{hndl}->DESTROY if defined $href->{hndl};
+ $href->{name}->DESTROY if defined $href->{name};
+ }
}
}
}
sub upload_hook {
- my ($self,$hook,$data) = self_or_default(@_);
+ my $self;
+ if (ref $_[0] eq 'CODE') {
+ $CGI::Q = $self = $CGI::DefaultClass->new(@_);
+ } else {
+ $self = shift;
+ }
+ my ($hook,$data) = @_;
$self->{'.upload_hook'} = $hook;
$self->{'.upload_data'} = $data;
}
if (($POST_MAX > 0) && ($content_length > $POST_MAX)) {
# quietly read and discard the post
my $buffer;
- my $max = $content_length;
- while ($max > 0 &&
- (my $bytes = $MOD_PERL
- ? $self->r->read($buffer,$max < 10000 ? $max : 10000)
- : read(STDIN,$buffer,$max < 10000 ? $max : 10000)
- )) {
- $self->cgi_error("413 Request entity too large");
- last METHOD;
- }
- }
+ my $tmplength = $content_length;
+ while($tmplength > 0) {
+ my $maxbuffer = ($tmplength < 10000)?$tmplength:10000;
+ my $bytesread = $MOD_PERL ? $self->r->read($buffer,$maxbuffer) : read(STDIN,$buffer,$maxbuffer);
+ $tmplength -= $bytesread;
+ }
+ $self->cgi_error("413 Request entity too large");
+ last METHOD;
+ }
# Process multipart postings, but only if the initializer is
# not defined.
my($sub) = \%{"$pack\:\:SUBS"};
unless (%$sub) {
my($auto) = \${"$pack\:\:AUTOLOADED_ROUTINES"};
+ local ($@,$!);
eval "package $pack; $$auto";
croak("$AUTOLOAD: $@") if $@;
$$auto = ''; # Free the unneeded storage (but don't undef it!!!)
}
}
croak("Undefined subroutine $AUTOLOAD\n") unless $code;
+ local ($@,$!);
eval "package $pack; $code";
if ($@) {
$@ =~ s/ at .*\n//;
my $self = shift;
my $value = shift;
return '' unless $value;
- return $XHTML ? qq( selected="selected") : qq( selected);
+ return $XHTML ? qq(selected="selected" ) : qq(selected );
}
sub _checked {
my $self = shift;
my $value = shift;
return '' unless $value;
- return $XHTML ? qq( checked="checked") : qq( checked);
+ return $XHTML ? qq(checked="checked" ) : qq(checked );
}
sub _reset_globals { initialize_globals(); }
$XHTML=0, next if /^[:-]no_?xhtml$/;
$USE_PARAM_SEMICOLONS=0, next if /^[:-]oldstyle_urls$/;
$PRIVATE_TEMPFILES++, next if /^[:-]private_tempfiles$/;
+ $TABINDEX++, next if /^[:-]tabindex$/;
$CLOSE_UPLOAD_FILES++, next if /^[:-]close_upload_files$/;
$EXPORT{$_}++, next if /^[:-]any$/;
$compile++, next if /^[:-]compile$/;
$self->{'.charset'};
}
+sub element_id {
+ my ($self,$new_value) = self_or_default(@_);
+ $self->{'.elid'} = $new_value if defined $new_value;
+ sprintf('%010d',$self->{'.elid'}++);
+}
+
+sub element_tab {
+ my ($self,$new_value) = self_or_default(@_);
+ $self->{'.etab'} ||= 1;
+ $self->{'.etab'} = $new_value if defined $new_value;
+ my $tab = $self->{'.etab'}++;
+ return '' unless $TABINDEX or defined $new_value;
+ return qq(tabindex="$tab" );
+}
+
###############################################################################
################# THESE FUNCTIONS ARE AUTOLOADED ON DEMAND ####################
###############################################################################
####
'append' => <<'EOF',
sub append {
- my($self,@p) = @_;
+ my($self,@p) = self_or_default(@_);
my($name,$value) = rearrange([NAME,[VALUE,VALUES]],@p);
my(@values) = defined($value) ? (ref($value) ? @{$value} : $value) : ();
if (@values) {
sub start_html {
my($self,@p) = &self_or_default(@_);
my($title,$author,$base,$xbase,$script,$noscript,
- $target,$meta,$head,$style,$dtd,$lang,$encoding,@other) =
- rearrange([TITLE,AUTHOR,BASE,XBASE,SCRIPT,NOSCRIPT,TARGET,META,HEAD,STYLE,DTD,LANG,ENCODING],@p);
+ $target,$meta,$head,$style,$dtd,$lang,$encoding,$declare_xml,@other) =
+ rearrange([TITLE,AUTHOR,BASE,XBASE,SCRIPT,NOSCRIPT,TARGET,
+ META,HEAD,STYLE,DTD,LANG,ENCODING,DECLARE_XML],@p);
+
+ $self->element_id(0);
+ $self->element_tab(0);
$encoding = 'iso-8859-1' unless defined $encoding;
$xml_dtd++ if ref($dtd) eq 'ARRAY' && $dtd->[0] =~ /\bXHTML\b/i;
$xml_dtd++ if ref($dtd) eq '' && $dtd =~ /\bXHTML\b/i;
- push @result,qq(<?xml version="1.0" encoding="$encoding"?>) if $xml_dtd;
+ push @result,qq(<?xml version="1.0" encoding="$encoding"?>) if $xml_dtd && $declare_xml;
if (ref($dtd) && ref($dtd) eq 'ARRAY') {
push(@result,qq(<!DOCTYPE html\n\tPUBLIC "$dtd->[0]"\n\t "$dtd->[1]">));
$lang = 'en-US' unless defined $lang;
}
- push(@result,$XHTML ? qq(<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang" xml:lang="$lang"><head><title>$title</title>)
- : ($lang ? qq(<html lang="$lang">) : "<html>")
+ my $lang_bits = $lang ne '' ? qq( lang="$lang" xml:lang="$lang") : '';
+ my $meta_bits = qq(<meta http-equiv="Content-Type" content="text/html; charset=$encoding" />)
+ if $XHTML && $encoding && !$declare_xml;
+
+ push(@result,$XHTML ? qq(<html xmlns="http://www.w3.org/1999/xhtml"$lang_bits>\n<head>\n<title>$title</title>)
+ : ($lang ? qq(<html lang="$lang">) : "<html>")
. "<head><title>$title</title>");
if (defined $author) {
push(@result,$XHTML ? "<link rev=\"made\" href=\"mailto:$author\" />"
- : "<link rev=\"made\" href=\"mailto:$author\">");
+ : "<link rev=\"made\" href=\"mailto:$author\">");
}
if ($base || $xbase || $target) {
# handle the infrequently-used -style and -script parameters
push(@result,$self->_style($style)) if defined $style;
push(@result,$self->_script($script)) if defined $script;
+ push(@result,$meta_bits) if defined $meta_bits;
# handle -noscript parameter
push(@result,<<END) if $noscript;
END
;
my($other) = @other ? " @other" : '';
- push(@result,"</head><body$other>");
+ push(@result,"</head>\n<body$other>\n");
return join("\n",@result);
}
END_OF_FUNC
push(@satts,'src'=>$src) if $src;
push(@satts,'language'=>$language) unless defined $type;
push(@satts,'type'=>$type);
- $code = "$cdata_start$code$cdata_end" if defined $code;
- push(@result,script({@satts},$code || ''));
+ $code = $cdata_start . $code . $cdata_end if defined $code;
+ push(@result,$self->script({@satts},$code || ''));
}
@result;
}
####
'end_html' => <<'END_OF_FUNC',
sub end_html {
- return "</body></html>";
+ return "\n</body>\n</html>";
}
END_OF_FUNC
$action = $self->escapeHTML($action);
}
else {
- $action = $self->escapeHTML($self->url(-absolute=>1,-path=>1));
- if (exists $ENV{QUERY_STRING} && length($ENV{QUERY_STRING})>0) {
- $action .= "?".$self->escapeHTML($ENV{QUERY_STRING},1);
- }
+ $action = $self->escapeHTML($self->request_uri);
}
$action = qq(action="$action");
my($other) = @other ? " @other" : '';
# synonym for startform
'start_form' => <<'END_OF_FUNC',
sub start_form {
- &startform;
+ $XHTML ? &start_multipart_form : &startform;
}
END_OF_FUNC
'start_multipart_form' => <<'END_OF_FUNC',
sub start_multipart_form {
my($self,@p) = self_or_default(@_);
- if (defined($param[0]) && substr($param[0],0,1) eq '-') {
+ if (defined($p[0]) && substr($p[0],0,1) eq '-') {
my(%p) = @p;
$p{'-enctype'}=&MULTIPART;
return $self->startform(%p);
# End a form
'endform' => <<'END_OF_FUNC',
sub endform {
- my($self,@p) = self_or_default(@_);
+ my($self,@p) = self_or_default(@_);
if ( $NOSTICKY ) {
return wantarray ? ("</form>") : "\n</form>";
} else {
- return wantarray ? ("<div>",$self->get_fields,"</div>","</form>") :
- "<div>".$self->get_fields ."</div>\n</form>";
+ if (my @fields = $self->get_fields) {
+ return wantarray ? ("<div>",@fields,"</div>","</form>")
+ : "<div>".(join '',@fields)."</div>\n</form>";
+ } else {
+ return "</form>";
+ }
}
}
END_OF_FUNC
'_textfield' => <<'END_OF_FUNC',
sub _textfield {
my($self,$tag,@p) = self_or_default(@_);
- my($name,$default,$size,$maxlength,$override,@other) =
- rearrange([NAME,[DEFAULT,VALUE,VALUES],SIZE,MAXLENGTH,[OVERRIDE,FORCE]],@p);
+ my($name,$default,$size,$maxlength,$override,$tabindex,@other) =
+ rearrange([NAME,[DEFAULT,VALUE,VALUES],SIZE,MAXLENGTH,[OVERRIDE,FORCE],TABINDEX],@p);
my $current = $override ? $default :
(defined($self->param($name)) ? $self->param($name) : $default);
# this entered at cristy's request to fix problems with file upload fields
# and WebTV -- not sure it won't break stuff
my($value) = $current ne '' ? qq(value="$current") : '';
- return $XHTML ? qq(<input type="$tag" name="$name" $value$s$m$other />)
+ $tabindex = $self->element_tab($tabindex);
+ return $XHTML ? qq(<input type="$tag" name="$name" $tabindex$value$s$m$other />)
: qq(<input type="$tag" name="$name" $value$s$m$other>);
}
END_OF_FUNC
'textarea' => <<'END_OF_FUNC',
sub textarea {
my($self,@p) = self_or_default(@_);
-
- my($name,$default,$rows,$cols,$override,@other) =
- rearrange([NAME,[DEFAULT,VALUE],ROWS,[COLS,COLUMNS],[OVERRIDE,FORCE]],@p);
+ my($name,$default,$rows,$cols,$override,$tabindex,@other) =
+ rearrange([NAME,[DEFAULT,VALUE],ROWS,[COLS,COLUMNS],[OVERRIDE,FORCE],TABINDEX],@p);
my($current)= $override ? $default :
(defined($self->param($name)) ? $self->param($name) : $default);
my($r) = $rows ? qq/ rows="$rows"/ : '';
my($c) = $cols ? qq/ cols="$cols"/ : '';
my($other) = @other ? " @other" : '';
- return qq{<textarea name="$name"$r$c$other>$current</textarea>};
+ $tabindex = $self->element_tab($tabindex);
+ return qq{<textarea name="$name" $tabindex$r$c$other>$current</textarea>};
}
END_OF_FUNC
sub button {
my($self,@p) = self_or_default(@_);
- my($label,$value,$script,@other) = rearrange([NAME,[VALUE,LABEL],
- [ONCLICK,SCRIPT]],@p);
+ my($label,$value,$script,$tabindex,@other) = rearrange([NAME,[VALUE,LABEL],
+ [ONCLICK,SCRIPT],TABINDEX],@p);
$label=$self->escapeHTML($label);
$value=$self->escapeHTML($value,1);
$val = qq/ value="$value"/ if $value;
$script = qq/ onclick="$script"/ if $script;
my($other) = @other ? " @other" : '';
- return $XHTML ? qq(<input type="button"$name$val$script$other />)
+ $tabindex = $self->element_tab($tabindex);
+ return $XHTML ? qq(<input type="button" $tabindex$name$val$script$other />)
: qq(<input type="button"$name$val$script$other>);
}
END_OF_FUNC
sub submit {
my($self,@p) = self_or_default(@_);
- my($label,$value,@other) = rearrange([NAME,[VALUE,LABEL]],@p);
+ my($label,$value,$tabindex,@other) = rearrange([NAME,[VALUE,LABEL],TABINDEX],@p);
$label=$self->escapeHTML($label);
$value=$self->escapeHTML($value,1);
- my $name = $NOSTICKY ? '' : ' name=".submit"';
- $name = qq/ name="$label"/ if defined($label);
+ my $name = $NOSTICKY ? '' : 'name=".submit" ';
+ $name = qq/name="$label" / if defined($label);
$value = defined($value) ? $value : $label;
my $val = '';
- $val = qq/ value="$value"/ if defined($value);
- my($other) = @other ? " @other" : '';
- return $XHTML ? qq(<input type="submit"$name$val$other />)
- : qq(<input type="submit"$name$val$other>);
+ $val = qq/value="$value" / if defined($value);
+ $tabindex = $self->element_tab($tabindex);
+ my($other) = @other ? "@other " : '';
+ return $XHTML ? qq(<input type="submit" $tabindex$name$val$other/>)
+ : qq(<input type="submit" $name$val$other>);
}
END_OF_FUNC
'reset' => <<'END_OF_FUNC',
sub reset {
my($self,@p) = self_or_default(@_);
- my($label,$value,@other) = rearrange(['NAME',['VALUE','LABEL']],@p);
+ my($label,$value,$tabindex,@other) = rearrange(['NAME',['VALUE','LABEL'],TABINDEX],@p);
$label=$self->escapeHTML($label);
$value=$self->escapeHTML($value,1);
my ($name) = ' name=".reset"';
my($val) = '';
$val = qq/ value="$value"/ if defined($value);
my($other) = @other ? " @other" : '';
- return $XHTML ? qq(<input type="reset"$name$val$other />)
+ $tabindex = $self->element_tab($tabindex);
+ return $XHTML ? qq(<input type="reset" $tabindex$name$val$other />)
: qq(<input type="reset"$name$val$other>);
}
END_OF_FUNC
sub defaults {
my($self,@p) = self_or_default(@_);
- my($label,@other) = rearrange([[NAME,VALUE]],@p);
+ my($label,$tabindex,@other) = rearrange([[NAME,VALUE],TABINDEX],@p);
$label=$self->escapeHTML($label,1);
$label = $label || "Defaults";
my($value) = qq/ value="$label"/;
my($other) = @other ? " @other" : '';
- return $XHTML ? qq(<input type="submit" name=".defaults"$value$other />)
+ $tabindex = $self->element_tab($tabindex);
+ return $XHTML ? qq(<input type="submit" name=".defaults" $tabindex$value$other />)
: qq/<input type="submit" NAME=".defaults"$value$other>/;
}
END_OF_FUNC
sub checkbox {
my($self,@p) = self_or_default(@_);
- my($name,$checked,$value,$label,$override,@other) =
- rearrange([NAME,[CHECKED,SELECTED,ON],VALUE,LABEL,[OVERRIDE,FORCE]],@p);
-
+ my($name,$checked,$value,$label,$override,$tabindex,@other) =
+ rearrange([NAME,[CHECKED,SELECTED,ON],VALUE,LABEL,[OVERRIDE,FORCE],TABINDEX],@p);
+
$value = defined $value ? $value : 'on';
if (!$override && ($self->{'.fieldnames'}->{$name} ||
$name = $self->escapeHTML($name);
$value = $self->escapeHTML($value,1);
$the_label = $self->escapeHTML($the_label);
- my($other) = @other ? " @other" : '';
+ my($other) = @other ? "@other " : '';
+ $tabindex = $self->element_tab($tabindex);
$self->register_parameter($name);
- return $XHTML ? qq{<input type="checkbox" name="$name" value="$value"$checked$other />$the_label}
+ return $XHTML ? CGI::label(qq{<input type="checkbox" name="$name" value="$value" $tabindex$checked$other/>$the_label})
: qq{<input type="checkbox" name="$name" value="$value"$checked$other>$the_label};
}
END_OF_FUNC
-#### Method: checkbox_group
-# Create a list of logically-linked checkboxes.
-# Parameters:
-# $name -> Common name for all the check boxes
-# $values -> A pointer to a regular array containing the
-# values for each checkbox in the group.
-# $defaults -> (optional)
-# 1. If a pointer to a regular array of checkbox values,
-# then this will be used to decide which
-# checkboxes to turn on by default.
-# 2. If a scalar, will be assumed to hold the
-# value of a single checkbox in the group to turn on.
-# $linebreak -> (optional) Set to true to place linebreaks
-# between the buttons.
-# $labels -> (optional)
-# A pointer to an associative array of labels to print next to each checkbox
-# in the form $label{'value'}="Long explanatory label".
-# Otherwise the provided values are used as the labels.
-# Returns:
-# An ARRAY containing a series of <input type="checkbox"> fields
-####
-'checkbox_group' => <<'END_OF_FUNC',
-sub checkbox_group {
- my($self,@p) = self_or_default(@_);
-
- my($name,$values,$defaults,$linebreak,$labels,$attributes,$rows,$columns,
- $rowheaders,$colheaders,$override,$nolabels,@other) =
- rearrange([NAME,[VALUES,VALUE],[DEFAULTS,DEFAULT],
- LINEBREAK,LABELS,ATTRIBUTES,ROWS,[COLUMNS,COLS],
- ROWHEADERS,COLHEADERS,
- [OVERRIDE,FORCE],NOLABELS],@p);
-
- my($checked,$break,$result,$label);
-
- my(%checked) = $self->previous_or_default($name,$defaults,$override);
-
- if ($linebreak) {
- $break = $XHTML ? "<br />" : "<br>";
- }
- else {
- $break = '';
- }
- $name=$self->escapeHTML($name);
-
- # Create the elements
- my(@elements,@values);
-
- @values = $self->_set_values_and_labels($values,\$labels,$name);
-
- my($other) = @other ? " @other" : '';
- foreach (@values) {
- $checked = $self->_checked($checked{$_});
- $label = '';
- unless (defined($nolabels) && $nolabels) {
- $label = $_;
- $label = $labels->{$_} if defined($labels) && defined($labels->{$_});
- $label = $self->escapeHTML($label);
- }
- my $attribs = $self->_set_attributes($_, $attributes);
- $_ = $self->escapeHTML($_,1);
- push(@elements,$XHTML ? qq(<input type="checkbox" name="$name" value="$_"$checked$other$attribs />${label}${break})
- : qq/<input type="checkbox" name="$name" value="$_"$checked$other$attribs>${label}${break}/);
- }
- $self->register_parameter($name);
- return wantarray ? @elements : join(' ',@elements)
- unless defined($columns) || defined($rows);
- $rows = 1 if $rows && $rows < 1;
- $cols = 1 if $cols && $cols < 1;
- return _tableize($rows,$columns,$rowheaders,$colheaders,@elements);
-}
-END_OF_FUNC
# Escape HTML -- used internally
'escapeHTML' => <<'END_OF_FUNC',
'_tableize' => <<'END_OF_FUNC',
sub _tableize {
my($rows,$columns,$rowheaders,$colheaders,@elements) = @_;
- $rowheaders = [] unless defined $rowheaders;
- $colheaders = [] unless defined $colheaders;
+ my @rowheaders = $rowheaders ? @$rowheaders : ();
+ my @colheaders = $colheaders ? @$colheaders : ();
my($result);
if (defined($columns)) {
if (defined($rows)) {
$columns = int(0.99 + @elements/$rows) unless defined($columns);
}
-
+
# rearrange into a pretty table
$result = "<table>";
my($row,$column);
- unshift(@$colheaders,'') if @$colheaders && @$rowheaders;
- $result .= "<tr>" if @{$colheaders};
- foreach (@{$colheaders}) {
+ unshift(@colheaders,'') if @colheaders && @rowheaders;
+ $result .= "<tr>" if @colheaders;
+ foreach (@colheaders) {
$result .= "<th>$_</th>";
}
for ($row=0;$row<$rows;$row++) {
$result .= "<tr>";
- $result .= "<th>$rowheaders->[$row]</th>" if @$rowheaders;
+ $result .= "<th>$rowheaders[$row]</th>" if @rowheaders;
for ($column=0;$column<$columns;$column++) {
$result .= "<td>" . $elements[$column*$rows + $row] . "</td>"
if defined($elements[$column*$rows + $row]);
'radio_group' => <<'END_OF_FUNC',
sub radio_group {
my($self,@p) = self_or_default(@_);
+ $self->_box_group('radio',@p);
+}
+END_OF_FUNC
+
+#### Method: checkbox_group
+# Create a list of logically-linked checkboxes.
+# Parameters:
+# $name -> Common name for all the check boxes
+# $values -> A pointer to a regular array containing the
+# values for each checkbox in the group.
+# $defaults -> (optional)
+# 1. If a pointer to a regular array of checkbox values,
+# then this will be used to decide which
+# checkboxes to turn on by default.
+# 2. If a scalar, will be assumed to hold the
+# value of a single checkbox in the group to turn on.
+# $linebreak -> (optional) Set to true to place linebreaks
+# between the buttons.
+# $labels -> (optional)
+# A pointer to an associative array of labels to print next to each checkbox
+# in the form $label{'value'}="Long explanatory label".
+# Otherwise the provided values are used as the labels.
+# Returns:
+# An ARRAY containing a series of <input type="checkbox"> fields
+####
+
+'checkbox_group' => <<'END_OF_FUNC',
+sub checkbox_group {
+ my($self,@p) = self_or_default(@_);
+ $self->_box_group('checkbox',@p);
+}
+END_OF_FUNC
+
+'_box_group' => <<'END_OF_FUNC',
+sub _box_group {
+ my $self = shift;
+ my $box_type = shift;
- my($name,$values,$default,$linebreak,$labels,$attributes,
- $rows,$columns,$rowheaders,$colheaders,$override,$nolabels,@other) =
- rearrange([NAME,[VALUES,VALUE],DEFAULT,LINEBREAK,LABELS,ATTRIBUTES,
- ROWS,[COLUMNS,COLS],
- ROWHEADERS,COLHEADERS,
- [OVERRIDE,FORCE],NOLABELS],@p);
+ my($name,$values,$defaults,$linebreak,$labels,$attributes,
+ $rows,$columns,$rowheaders,$colheaders,
+ $override,$nolabels,$tabindex,@other) =
+ rearrange([ NAME,[VALUES,VALUE],[DEFAULT,DEFAULTS],LINEBREAK,LABELS,ATTRIBUTES,
+ ROWS,[COLUMNS,COLS],ROWHEADERS,COLHEADERS,
+ [OVERRIDE,FORCE],NOLABELS,TABINDEX
+ ],@_);
my($result,$checked);
- if (!$override && defined($self->param($name))) {
- $checked = $self->param($name);
- } else {
- $checked = $default;
- }
+
my(@elements,@values);
@values = $self->_set_values_and_labels($values,\$labels,$name);
+ my %checked = $self->previous_or_default($name,$defaults,$override);
# If no check array is specified, check the first by default
- $checked = $values[0] unless defined($checked) && $checked ne '';
+ $checked{$values[0]}++ if $box_type eq 'radio' && !%checked;
+
$name=$self->escapeHTML($name);
- my($other) = @other ? " @other" : '';
+ my %tabs = ();
+ if ($TABINDEX && $tabindex) {
+ if (!ref $tabindex) {
+ $self->element_tab($tabindex);
+ } elsif (ref $tabindex eq 'ARRAY') {
+ %tabs = map {$_=>$self->element_tab} @$tabindex;
+ } elsif (ref $tabindex eq 'HASH') {
+ %tabs = %$tabindex;
+ }
+ }
+ %tabs = map {$_=>$self->element_tab} @values unless %tabs;
+
+ my $other = @other ? "@other " : '';
+ my $radio_checked;
foreach (@values) {
- my($checkit) = $checked eq $_ ? qq/ checked="checked"/ : '';
+ my $checkit = $self->_checked($box_type eq 'radio' ? ($checked{$_} && !$radio_checked++)
+ : $checked{$_});
my($break);
if ($linebreak) {
$break = $XHTML ? "<br />" : "<br>";
$label = $labels->{$_} if defined($labels) && defined($labels->{$_});
$label = $self->escapeHTML($label,1);
}
- my $attribs = $self->_set_attributes($_, $attributes);
+ my $attribs = $self->_set_attributes($_, $attributes);
+ my $tab = $tabs{$_};
$_=$self->escapeHTML($_);
- push(@elements,$XHTML ? qq(<input type="radio" name="$name" value="$_"$checkit$other$attribs />${label}${break})
- : qq/<input type="radio" name="$name" value="$_"$checkit$other$attribs>${label}${break}/);
+ if ($XHTML) {
+ push @elements,
+ CGI::label(
+ qq(<input type="$box_type" name="$name" value="$_" $checkit$other$tab$attribs/>$label)).${break};
+ } else {
+ push(@elements,qq/<input type="$box_type" name="$name" value="$_"$checkit$other$tab$attribs>${label}${break}/);
+ }
}
$self->register_parameter($name);
- return wantarray ? @elements : join(' ',@elements)
+ return wantarray ? @elements : "@elements"
unless defined($columns) || defined($rows);
return _tableize($rows,$columns,$rowheaders,$colheaders,@elements);
}
sub popup_menu {
my($self,@p) = self_or_default(@_);
- my($name,$values,$default,$labels,$attributes,$override,@other) =
+ my($name,$values,$default,$labels,$attributes,$override,$tabindex,@other) =
rearrange([NAME,[VALUES,VALUE],[DEFAULT,DEFAULTS],LABELS,
- ATTRIBUTES,[OVERRIDE,FORCE]],@p);
+ ATTRIBUTES,[OVERRIDE,FORCE],TABINDEX],@p);
my($result,$selected);
if (!$override && defined($self->param($name))) {
my(@values);
@values = $self->_set_values_and_labels($values,\$labels,$name);
-
- $result = qq/<select name="$name"$other>\n/;
+ $tabindex = $self->element_tab($tabindex);
+ $result = qq/<select name="$name" $tabindex$other>\n/;
foreach (@values) {
if (/<optgroup/) {
foreach (split(/\n/)) {
$label = $labels->{$_} if defined($labels) && defined($labels->{$_});
my($value) = $self->escapeHTML($_);
$label=$self->escapeHTML($label,1);
- $result .= "<option$selectit$attribs value=\"$value\">$label</option>\n";
+ $result .= "<option $selectit${attribs}value=\"$value\">$label</option>\n";
}
}
'scrolling_list' => <<'END_OF_FUNC',
sub scrolling_list {
my($self,@p) = self_or_default(@_);
- my($name,$values,$defaults,$size,$multiple,$labels,$attributes,$override,@other)
+ my($name,$values,$defaults,$size,$multiple,$labels,$attributes,$override,$tabindex,@other)
= rearrange([NAME,[VALUES,VALUE],[DEFAULTS,DEFAULT],
- SIZE,MULTIPLE,LABELS,ATTRIBUTES,[OVERRIDE,FORCE]],@p);
+ SIZE,MULTIPLE,LABELS,ATTRIBUTES,[OVERRIDE,FORCE],TABINDEX],@p);
my($result,@values);
@values = $self->_set_values_and_labels($values,\$labels,$name);
my($other) = @other ? " @other" : '';
$name=$self->escapeHTML($name);
- $result = qq/<select name="$name"$has_size$is_multiple$other>\n/;
+ $tabindex = $self->element_tab($tabindex);
+ $result = qq/<select name="$name" $tabindex$has_size$is_multiple$other>\n/;
foreach (@values) {
my($selectit) = $self->_selected($selected{$_});
my($label) = $_;
$label=$self->escapeHTML($label);
my($value)=$self->escapeHTML($_,1);
my $attribs = $self->_set_attributes($_, $attributes);
- $result .= "<option$selectit$attribs value=\"$value\">$label</option>\n";
+ $result .= "<option ${selectit}${attribs}value=\"$value\">$label</option>\n";
}
$result .= "</select>";
$self->register_parameter($name);
'url' => <<'END_OF_FUNC',
sub url {
my($self,@p) = self_or_default(@_);
- my ($relative,$absolute,$full,$path_info,$query,$base) =
- rearrange(['RELATIVE','ABSOLUTE','FULL',['PATH','PATH_INFO'],['QUERY','QUERY_STRING'],'BASE'],@p);
- my $url;
+ my ($relative,$absolute,$full,$path_info,$query,$base,$rewrite) =
+ rearrange(['RELATIVE','ABSOLUTE','FULL',['PATH','PATH_INFO'],['QUERY','QUERY_STRING'],'BASE','REWRITE'],@p);
+ my $url = '';
$full++ if $base || !($relative || $absolute);
+ $rewrite++ unless defined $rewrite;
- my $path = $self->path_info;
- my $script_name = $self->script_name;
-
- # for compatibility with Apache's MultiViews
- if (exists($ENV{REQUEST_URI})) {
- my $index;
- $script_name = unescape($ENV{REQUEST_URI});
- $script_name =~ s/\?.+$//s; # strip query string
- # and path
- if (exists($ENV{PATH_INFO})) {
- my $encoded_path = unescape($ENV{PATH_INFO});
- $script_name =~ s/\Q$encoded_path\E$//i;
- }
- }
+ my $path = $self->path_info;
+ my $script_name = $self->script_name;
+ my $request_uri = $self->request_uri || '';
+ my $query_str = $self->query_string;
+
+ my $rewrite_in_use = $request_uri && $request_uri !~ /^$script_name/;
+ undef $path if $rewrite_in_use && $rewrite; # path not valid when rewriting active
+
+ my $uri = $rewrite && $request_uri ? $request_uri : $script_name;
+ $uri =~ s/\?.*$//; # remove query string
+ $uri =~ s/$path$// if defined $path; # remove path
if ($full) {
my $protocol = $self->protocol();
|| (lc($protocol) eq 'https' && $port == 443);
}
return $url if $base;
- $url .= $script_name;
+ $url .= $uri;
} elsif ($relative) {
($url) = $script_name =~ m!([^/]+)$!;
} elsif ($absolute) {
- $url = $script_name;
+ $url = $uri;
}
- $url .= $path if $path_info and defined $path;
- $url .= "?" . $self->query_string if $query and $self->query_string;
- $url = '' unless defined $url;
+ $url .= $path if $path_info and defined $path;
+ $url .= "?$query_str" if $query and $query_str ne '';
$url =~ s/([^a-zA-Z0-9_.%;&?\/\\:+=~-])/sprintf("%%%02X",ord($1))/eg;
return $url;
}
$info = "/$info" if $info ne '' && substr($info,0,1) ne '/';
$self->{'.path_info'} = $info;
} elsif (! defined($self->{'.path_info'}) ) {
- $self->{'.path_info'} = defined($ENV{'PATH_INFO'}) ?
- $ENV{'PATH_INFO'} : '';
-
+ my (undef,$path_info) = $self->_name_and_path_from_env;
+ $self->{'.path_info'} = $path_info || '';
# hack to fix broken path info in IIS
$self->{'.path_info'} =~ s/^\Q$ENV{'SCRIPT_NAME'}\E// if $IIS;
}
END_OF_FUNC
+# WE USE THIS TO COMPENSATE FOR A BUG IN APACHE 2 PRESENT AT LEAST UP THROUGH 2.0.54
+'_name_and_path_from_env' => <<'END_OF_FUNC',
+sub _name_and_path_from_env {
+ my $self = shift;
+ my $raw_script_name = $ENV{SCRIPT_NAME} || '';
+ my $raw_path_info = $ENV{PATH_INFO} || '';
+ my $uri = $ENV{REQUEST_URI} || '';
+
+ if ($raw_script_name =~ m/$raw_path_info$/) {
+ $raw_script_name =~ s/$raw_path_info$//;
+ }
+
+ my @uri_double_slashes = $uri =~ m^(/{2,}?)^g;
+ my @path_double_slashes = "$raw_script_name $raw_path_info" =~ m^(/{2,}?)^g;
+
+ my $apache_bug = @uri_double_slashes != @path_double_slashes;
+ return ($raw_script_name,$raw_path_info) unless $apache_bug;
+
+ my $path_info_search = $raw_path_info;
+ # these characters will not (necessarily) be escaped
+ $path_info_search =~ s/([^a-zA-Z0-9$()':_.,+*\/;?=&-])/uc sprintf("%%%02x",ord($1))/eg;
+ $path_info_search = quotemeta($path_info_search);
+ $path_info_search =~ s!/!/+!g;
+ if ($uri =~ m/^(.+)($path_info_search)/) {
+ return ($1,$2);
+ } else {
+ return ($raw_script_name,$raw_path_info);
+ }
+}
+END_OF_FUNC
+
#### Method: request_method
# Returns 'POST', 'GET', 'PUT' or 'HEAD'
END_OF_FUNC
+#### Method: request_uri
+# Return the literal request URI
+####
+'request_uri' => <<'END_OF_FUNC',
+sub request_uri {
+ return $ENV{'REQUEST_URI'};
+}
+END_OF_FUNC
+
+
#### Method: query_string
# Synthesize a query string from our current
# parameters
####
'script_name' => <<'END_OF_FUNC',
sub script_name {
- return $ENV{'SCRIPT_NAME'} if defined($ENV{'SCRIPT_NAME'});
- # These are for debugging
- return "/$0" unless $0=~/^\//;
- return $0;
+ my ($self,@p) = self_or_default(@_);
+ if (@p) {
+ $self->{'.script_name'} = shift;
+ } elsif (!exists $self->{'.script_name'}) {
+ my ($script_name,$path_info) = $self->_name_and_path_from_env();
+ $self->{'.script_name'} = $script_name;
+ }
+ return $self->{'.script_name'};
}
END_OF_FUNC
sub virtual_port {
my($self) = self_or_default(@_);
my $vh = $self->http('x_forwarded_host') || $self->http('host');
+ my $protocol = $self->protocol;
if ($vh) {
- return ($vh =~ /:(\d+)$/)[0] || '80';
+ return ($vh =~ /:(\d+)$/)[0] || ($protocol eq 'https' ? 443 : 80);
} else {
return $self->server_port();
}
}
# choose a relatively unpredictable tmpfile sequence number
- my $seqno = unpack("%16C*",join('',localtime,values %ENV));
+ my $seqno = unpack("%16C*",join('',localtime,grep {defined $_} values %ENV));
for (my $cnt=10;$cnt>0;$cnt--) {
next unless $tmpfile = new CGITempFile($seqno);
$tmp = $tmpfile->as_string;
# Save some information about the uploaded file where we can get
# at it later.
- $self->{'.tmpfiles'}->{fileno($filehandle)}= {
+ # Use the typeglob as the key, as this is guaranteed to be
+ # unique for each filehandle. Don't use the file descriptor as
+ # this will be re-used for each filehandle if the
+ # close_upload_files feature is used.
+ $self->{'.tmpfiles'}->{$$filehandle}= {
hndl => $filehandle,
name => $tmpfile,
info => {%header},
'tmpFileName' => <<'END_OF_FUNC',
sub tmpFileName {
my($self,$filename) = self_or_default(@_);
- return $self->{'.tmpfiles'}->{fileno($filename)}->{name} ?
- $self->{'.tmpfiles'}->{fileno($filename)}->{name}->as_string
+ return $self->{'.tmpfiles'}->{$$filename}->{name} ?
+ $self->{'.tmpfiles'}->{$$filename}->{name}->as_string
: '';
}
END_OF_FUNC
'uploadInfo' => <<'END_OF_FUNC',
sub uploadInfo {
my($self,$filename) = self_or_default(@_);
- return $self->{'.tmpfiles'}->{fileno($filename)}->{info};
+ return $self->{'.tmpfiles'}->{$$filename}->{info};
}
END_OF_FUNC
*Fh::AUTOLOAD = \&CGI::AUTOLOAD;
+sub DESTROY {
+ my $self = shift;
+ close $self;
+}
+
$AUTOLOADED_ROUTINES = ''; # prevent -w error
$AUTOLOADED_ROUTINES=<<'END_OF_AUTOLOAD';
%SUBS = (
}
END_OF_FUNC
-'DESTROY' => <<'END_OF_FUNC',
-sub DESTROY {
- my $self = shift;
- close $self;
-}
-END_OF_FUNC
-
);
END_OF_AUTOLOAD
my($package,$interface,$boundary,$length) = @_;
$FILLUNIT = $INITIAL_FILLUNIT;
$CGI::DefaultClass->binmode($IN); # if $CGI::needs_binmode; # just do it always
-
+
# If the user types garbage into the file upload field,
# then Netscape passes NOTHING to the server (not good).
# We may hang on this read in that case. So we implement
}
my $self = {LENGTH=>$length,
+ CHUNKED=>!defined $length,
BOUNDARY=>$boundary,
INTERFACE=>$interface,
BUFFER=>'',
my $start = index($self->{BUFFER},$boundary_start);
warn "boundary=$self->{BOUNDARY} length=$self->{LENGTH} start=$start\n" if DEBUG;
- # protect against malformed multipart POST operations
- die "Malformed multipart POST\n" unless ($start >= 0) || ($self->{LENGTH} > 0);
+ # protect against malformed multipart POST operations
+ die "Malformed multipart POST\n" unless $self->{CHUNKED} || ($start >= 0 || $self->{LENGTH} > 0);
#EBCDIC NOTE: want to translate boundary search into ASCII here.
'fillBuffer' => <<'END_OF_FUNC',
sub fillBuffer {
my($self,$bytes) = @_;
- return unless $self->{LENGTH};
+ return unless $self->{CHUNKED} || $self->{LENGTH};
my($boundaryLength) = length($self->{BOUNDARY});
my($bufferLength) = length($self->{BUFFER});
my($bytesToRead) = $bytes - $bufferLength + $boundaryLength + 2;
- $bytesToRead = $self->{LENGTH} if $self->{LENGTH} < $bytesToRead;
+ $bytesToRead = $self->{LENGTH} if !$self->{CHUNKED} && $self->{LENGTH} < $bytesToRead;
# Try to read some data. We may hang here if the browser is screwed up.
my $bytesRead = $self->{INTERFACE}->read_from_client(\$self->{BUFFER},
# remote user aborts during a file transfer. I don't know how
# they manage this, but the workaround is to abort if we get
# more than SPIN_LOOP_MAX consecutive zero reads.
- if ($bytesRead == 0) {
+ if ($bytesRead <= 0) {
die "CGI.pm: Server closed socket during multipart read (client aborted?).\n"
if ($self->{ZERO_LOOP_COUNTER}++ >= $SPIN_LOOP_MAX);
} else {
$self->{ZERO_LOOP_COUNTER}=0;
}
- $self->{LENGTH} -= $bytesRead;
+ $self->{LENGTH} -= $bytesRead if !$self->{CHUNKED} && $bytesRead;
}
END_OF_FUNC
package CGITempFile;
sub find_tempdir {
- undef $TMPDIRECTORY;
$SL = $CGI::SL;
$MAC = $CGI::OS eq 'MACINTOSH';
my ($vol) = $MAC ? MacPerl::Volumes() =~ /:(.*)/ : "";
- unless ($TMPDIRECTORY) {
+ unless (defined $TMPDIRECTORY) {
@TEMP=("${SL}usr${SL}tmp","${SL}var${SL}tmp",
"C:${SL}temp","${SL}tmp","${SL}temp",
"${vol}${SL}Temporary Items",
hr;
if (param()) {
- print "Your name is",em(param('name')),p,
- "The keywords are: ",em(join(", ",param('words'))),p,
- "Your favorite color is ",em(param('color')),
+ my $name = param('name');
+ my $keywords = join ', ',param('words');
+ my $color = param('color');
+ print "Your name is",em(escapeHTML($name)),p,
+ "The keywords are: ",em(escapeHTML($keywords)),p,
+ "Your favorite color is ",em(escapeHTML($color)),
hr;
}
Use Delete_all() instead if you are using the function call interface.
+=head2 HANDLING NON-URLENCODED ARGUMENTS
+
+
+If POSTed data is not of type application/x-www-form-urlencoded or
+multipart/form-data, then the POSTed data will not be processed, but
+instead be returned as-is in a parameter named POSTDATA. To retrieve
+it, use code like this:
+
+ my $data = $query->param('POSTDATA');
+
+(If you don't know what the preceding means, don't worry about it. It
+only affects people trying to use CGI for XML processing and other
+specialized tasks.)
+
+
=head2 DIRECT ACCESS TO THE PARAMETER LIST:
$q->param_fetch('address')->[1] = '1313 Mockingbird Lane';
=head2 SAVING THE STATE OF THE SCRIPT TO A FILE:
- $query->save(FILEHANDLE)
+ $query->save(\*FILEHANDLE)
This will write the current state of the form to the provided
filehandle. You can read it back in by providing a filehandle
foreach (0..$records) {
my $q = new CGI;
$q->param(-name=>'counter',-value=>$_);
- $q->save(OUT);
+ $q->save(\*OUT);
}
close OUT;
# reopen for reading
open (IN,"test.out") || die;
while (!eof(IN)) {
- my $q = new CGI(IN);
+ my $q = new CGI(\*IN);
print $q->param('counter'),"\n";
}
=item -nosticky
-This makes CGI.pm not generating the hidden fields .submit
-and .cgifields. It is very useful if you don't want to
-have the hidden fields appear in the querystring in a GET method.
-For example, a search script generated this way will have
-a very nice url with search parameters for bookmarking.
+By default the CGI module implements a state-preserving behavior
+called "sticky" fields. The way this works is that if you are
+regenerating a form, the methods that generate the form field values
+will interrogate param() to see if similarly-named parameters are
+present in the query string. If they find a like-named parameter, they
+will use it to set their default values.
+
+Sometimes this isn't what you want. The B<-nosticky> pragma prevents
+this behavior. You can also selectively change the sticky behavior in
+each element that you generate.
+
+=item -tabindex
+
+Automatically add tab index attributes to each form field. With this
+option turned off, you can still add tab indexes manually by passing a
+-tabindex option to each field-generating method.
=item -no_undef_params
manipulated for special purposes, such as server push and pay per view
pages.
- print $query->header;
+ print header;
-or-
- print $query->header('image/gif');
+ print header('image/gif');
-or-
- print $query->header('text/html','204 No response');
+ print header('text/html','204 No response');
-or-
- print $query->header(-type=>'image/gif',
+ print header(-type=>'image/gif',
-nph=>1,
-status=>'402 Payment required',
-expires=>'+3d',
header fields, allowing you to specify any HTTP header you desire.
Internal underscores will be turned into hyphens:
- print $query->header(-Content_length=>3002);
+ print header(-Content_length=>3002);
Most browsers will not cache the output from CGI scripts. Every time
the browser reloads the page, the script is invoked anew. You can
=head2 GENERATING A REDIRECTION HEADER
- print $query->redirect('http://somewhere.else/in/movie/land');
+ print redirect('http://somewhere.else/in/movie/land');
Sometimes you don't want to produce a document yourself, but simply
redirect the browser elsewhere, perhaps choosing a URL based on the
You can also use named arguments:
- print $query->redirect(-uri=>'http://somewhere.else/in/movie/land',
+ print redirect(-uri=>'http://somewhere.else/in/movie/land',
-nph=>1,
-status=>301);
=head2 CREATING THE HTML DOCUMENT HEADER
- print $query->start_html(-title=>'Secrets of the Pyramids',
+ print start_html(-title=>'Secrets of the Pyramids',
-author=>'fred@capricorn.org',
-base=>'true',
-target=>'_blank',
The B<-encoding> argument can be used to specify the character set for
XHTML. It defaults to iso-8859-1 if not specified.
+The B<-declare_xml> argument, when used in conjunction with XHTML,
+will put a <?xml> declaration at the top of the HTML header. The sole
+purpose of this declaration is to declare the character set
+encoding. In the absence of -declare_xml, the output HTML will contain
+a <meta> tag that specifies the encoding, allowing the HTML to pass
+most validators. The default for -declare_xml is false.
+
You can place other arbitrary HTML elements to the <head> section with the
B<-head> tag. For example, to place the rarely-used <link> element in the
head section, use this:
B<-script> field:
$query = new CGI;
- print $query->header;
+ print header;
$JSCRIPT=<<END;
// Ask a silly question
function riddle_me_this() {
alert("Wrong! Guess again.");
}
END
- print $query->start_html(-title=>'The Riddle of the Sphinx',
+ print start_html(-title=>'The Riddle of the Sphinx',
-script=>$JSCRIPT);
Use the B<-noScript> parameter to pass some HTML text that will be displayed on
=head2 ENDING THE HTML DOCUMENT:
- print $query->end_html
+ print end_html
This ends an HTML document by printing the </body></html> tags.
=head2 CREATING A SELF-REFERENCING URL THAT PRESERVES STATE INFORMATION:
- $myself = $query->self_url;
+ $myself = self_url;
print q(<a href="$myself">I'm talking to myself.</a>);
self_url() will return a URL, that, when selected, will reinvoke
internal anchors but you don't want to disrupt the current contents
of the form(s). Something like this will do the trick.
- $myself = $query->self_url;
+ $myself = self_url;
print "<a href=\"$myself#table1\">See table 1</a>";
print "<a href=\"$myself#table2\">See table 2</a>";
print "<a href=\"$myself#yourself\">See for yourself</a>";
You can also retrieve the unprocessed query string with query_string():
- $the_string = $query->query_string;
+ $the_string = query_string;
=head2 OBTAINING THE SCRIPT'S URL
- $full_url = $query->url();
- $full_url = $query->url(-full=>1); #alternative syntax
- $relative_url = $query->url(-relative=>1);
- $absolute_url = $query->url(-absolute=>1);
- $url_with_path = $query->url(-path_info=>1);
- $url_with_path_and_query = $query->url(-path_info=>1,-query=>1);
- $netloc = $query->url(-base => 1);
+ $full_url = url();
+ $full_url = url(-full=>1); #alternative syntax
+ $relative_url = url(-relative=>1);
+ $absolute_url = url(-absolute=>1);
+ $url_with_path = url(-path_info=>1);
+ $url_with_path_and_query = url(-path_info=>1,-query=>1);
+ $netloc = url(-base => 1);
B<url()> returns the script's URL in a variety of formats. Called
without any arguments, it returns the full form of the URL, including
Generate just the protocol and net location, as in http://www.foo.com:8000
+=item B<-rewrite>
+
+If Apache's mod_rewrite is turned on, then the script name and path
+info probably won't match the request that the user sent. Set
+-rewrite=>1 (default) to return URLs that match what the user sent
+(the original request URI). Set -rewrite->0 to return URLs that match
+the URL after mod_rewrite's rules have run. Because the additional
+path information only makes sense in the context of the rewritten URL,
+-rewrite is set to false when you request path info in the URL.
+
=back
=head2 MIXING POST AND URL PARAMETERS
- $color = $query->url_param('color');
+ $color = url_param('color');
It is possible for a script to receive CGI parameters in the URL as
well as in the fill-out form by creating a form that POSTs to a URL
This example shows how to use the HTML methods:
- $q = new CGI;
print $q->blockquote(
"Many years ago on the island of",
$q->a({href=>"http://crete.org/"},"Crete"),
(2) use the -override (alias -force) parameter (a new feature in version 2.15).
This forces the default value to be used, regardless of the previous value:
- print $query->textfield(-name=>'field_name',
+ print textfield(-name=>'field_name',
-default=>'starting value',
-override=>1,
-size=>50,
autoEscape() method with a false value immediately after creating the CGI object:
$query = new CGI;
- $query->autoEscape(undef);
+ autoEscape(undef);
I<A Lurking Trap!> Some of the form-element generating methods return
multiple tags. In a scalar context, the tags will be concatenated
elements, allowing you to modify them if you wish. Usually you will
not notice this behavior, but beware of this:
- printf("%s\n",$query->end_form())
+ printf("%s\n",end_form())
end_form() produces several tags, and only the first of them will be
printed because the format only expects one value.
=head2 CREATING AN ISINDEX TAG
- print $query->isindex(-action=>$action);
+ print isindex(-action=>$action);
-or-
- print $query->isindex($action);
+ print isindex($action);
Prints out an <isindex> tag. Not very exciting. The parameter
-action specifies the URL of the script to process the query. The
=head2 STARTING AND ENDING A FORM
- print $query->start_form(-method=>$method,
- -action=>$action,
- -enctype=>$encoding);
+ print start_form(-method=>$method,
+ -action=>$action,
+ -enctype=>$encoding);
<... various form stuff ...>
- print $query->endform;
+ print endform;
-or-
- print $query->start_form($method,$action,$encoding);
+ print start_form($method,$action,$encoding);
<... various form stuff ...>
- print $query->endform;
+ print endform;
start_form() will return a <form> tag with the optional method,
action and form encoding that you specify. The defaults are:
by CGI scripts unless they use CGI.pm or another library designed
to handle them.
+If XHTML is activated (the default), then forms will be automatically
+created using this type of encoding.
+
=back
For compatibility, the start_form() method uses the older form of
block in the HTML header and -onSubmit points to one of these function
call. See start_html() for details.
+=head2 FORM ELEMENTS
+
+After starting a form, you will typically create one or more
+textfields, popup menus, radio groups and other form elements. Each
+of these elements takes a standard set of named arguments. Some
+elements also have optional arguments. The standard arguments are as
+follows:
+
+=over 4
+
+=item B<-name>
+
+The name of the field. After submission this name can be used to
+retrieve the field's value using the param() method.
+
+=item B<-value>, B<-values>
+
+The initial value of the field which will be returned to the script
+after form submission. Some form elements, such as text fields, take
+a single scalar -value argument. Others, such as popup menus, take a
+reference to an array of values. The two arguments are synonyms.
+
+=item B<-tabindex>
+
+A numeric value that sets the order in which the form element receives
+focus when the user presses the tab key. Elements with lower values
+receive focus first.
+
+=item B<-id>
+
+A string identifier that can be used to identify this element to
+JavaScript and DHTML.
+
+=item B<-override>
+
+A boolean, which, if true, forces the element to take on the value
+specified by B<-value>, overriding the sticky behavior described
+earlier for the B<-no_sticky> pragma.
+
+=item B<-onChange>, B<-onFocus>, B<-onBlur>, B<-onMouseOver>, B<-onMouseOut>, B<-onSelect>
+
+These are used to assign JavaScript event handlers. See the
+JavaScripting section for more details.
+
+=back
+
+Other common arguments are described in the next section. In addition
+to these, all attributes described in the HTML specifications are
+supported.
+
=head2 CREATING A TEXT FIELD
- print $query->textfield(-name=>'field_name',
- -default=>'starting value',
- -size=>50,
- -maxlength=>80);
+ print textfield(-name=>'field_name',
+ -value=>'starting value',
+ -size=>50,
+ -maxlength=>80);
-or-
- print $query->textfield('field_name','starting value',50,80);
+ print textfield('field_name','starting value',50,80);
-textfield() will return a text input field.
+textfield() will return a text input field.
=over 4
=item 1.
-The first parameter is the required name for the field (-name).
+The first parameter is the required name for the field (-name).
=item 2.
The optional second parameter is the default starting value for the field
-contents (-default).
+contents (-value, formerly known as -default).
=item 3.
When the form is processed, the value of the text field can be
retrieved with:
- $value = $query->param('foo');
+ $value = param('foo');
If you want to reset it from its initial value after the script has been
called once, you can do so like this:
- $query->param('foo',"I'm taking over this value!");
-
-NEW AS OF VERSION 2.15: If you don't want the field to take on its previous
-value, you can force its current value by using the -override (alias -force)
-parameter:
-
- print $query->textfield(-name=>'field_name',
- -default=>'starting value',
- -override=>1,
- -size=>50,
- -maxlength=>80);
-
-JAVASCRIPTING: You can also provide B<-onChange>, B<-onFocus>,
-B<-onBlur>, B<-onMouseOver>, B<-onMouseOut> and B<-onSelect>
-parameters to register JavaScript event handlers. The onChange
-handler will be called whenever the user changes the contents of the
-text field. You can do text validation if you like. onFocus and
-onBlur are called respectively when the insertion point moves into and
-out of the text field. onSelect is called when the user changes the
-portion of the text that is selected.
+ param('foo',"I'm taking over this value!");
=head2 CREATING A BIG TEXT FIELD
- print $query->textarea(-name=>'foo',
+ print textarea(-name=>'foo',
-default=>'starting value',
-rows=>10,
-columns=>50);
-or
- print $query->textarea('foo','starting value',10,50);
+ print textarea('foo','starting value',10,50);
textarea() is just like textfield, but it allows you to specify
rows and columns for a multiline text entry box. You can provide
a starting value for the field, which can be long and contain
multiple lines.
-JAVASCRIPTING: The B<-onChange>, B<-onFocus>, B<-onBlur> ,
-B<-onMouseOver>, B<-onMouseOut>, and B<-onSelect> parameters are
-recognized. See textfield().
-
=head2 CREATING A PASSWORD FIELD
- print $query->password_field(-name=>'secret',
+ print password_field(-name=>'secret',
-value=>'starting value',
-size=>50,
-maxlength=>80);
-or-
- print $query->password_field('secret','starting value',50,80);
+ print password_field('secret','starting value',50,80);
password_field() is identical to textfield(), except that its contents
will be starred out on the web page.
-JAVASCRIPTING: The B<-onChange>, B<-onFocus>, B<-onBlur>,
-B<-onMouseOver>, B<-onMouseOut> and B<-onSelect> parameters are
-recognized. See textfield().
-
=head2 CREATING A FILE UPLOAD FIELD
- print $query->filefield(-name=>'uploaded_file',
+ print filefield(-name=>'uploaded_file',
-default=>'starting value',
-size=>50,
-maxlength=>80);
-or-
- print $query->filefield('uploaded_file','starting value',50,80);
+ print filefield('uploaded_file','starting value',50,80);
filefield() will return a file upload field for Netscape 2.0 browsers.
In order to take full advantage of this I<you must use the new
When the form is processed, you can retrieve the entered filename
by calling param():
- $filename = $query->param('uploaded_file');
+ $filename = param('uploaded_file');
Different browsers will return slightly different things for the
name. Some browsers return the filename only. Others return the full
called with the name of an upload field, I<upload()> returns a
filehandle, or undef if the parameter is not a valid filehandle.
- $fh = $query->upload('uploaded_file');
+ $fh = upload('uploaded_file');
while (<$fh>) {
print;
}
retrieve this information, call uploadInfo(). It returns a reference to
an associative array containing all the document headers.
- $filename = $query->param('uploaded_file');
- $type = $query->uploadInfo($filename)->{'Content-Type'};
+ $filename = param('uploaded_file');
+ $type = uploadInfo($filename)->{'Content-Type'};
unless ($type eq 'text/html') {
die "HTML FILES ONLY!";
}
you can incorporate it into a status code to be sent to the browser.
Example:
- $file = $query->upload('uploaded_file');
- if (!$file && $query->cgi_error) {
- print $query->header(-status=>$query->cgi_error);
+ $file = upload('uploaded_file');
+ if (!$file && cgi_error) {
+ print header(-status=>cgi_error);
exit 0;
}
that the first argument to the callback is an Apache::Upload object,
here it's the remote filename.
- $q = CGI->new();
- $q->upload_hook(\&hook,$data);
+ $q = CGI->new(\&hook,$data);
sub hook
{
=head2 CREATING A POPUP MENU
- print $query->popup_menu('menu_name',
+ print popup_menu('menu_name',
['eenie','meenie','minie'],
'meenie');
'meenie'=>'your second choice',
'minie'=>'your third choice');
%attributes = ('eenie'=>{'class'=>'class of first choice'});
- print $query->popup_menu('menu_name',
+ print popup_menu('menu_name',
['eenie','meenie','minie'],
'meenie',\%labels,\%attributes);
-or (named parameter style)-
- print $query->popup_menu(-name=>'menu_name',
+ print popup_menu(-name=>'menu_name',
-values=>['eenie','meenie','minie'],
-default=>'meenie',
-labels=>\%labels,
When the form is processed, the selected value of the popup menu can
be retrieved using:
- $popup_menu_value = $query->param('menu_name');
-
-JAVASCRIPTING: popup_menu() recognizes the following event handlers:
-B<-onChange>, B<-onFocus>, B<-onMouseOver>, B<-onMouseOut>, and
-B<-onBlur>. See the textfield() section for details on when these
-handlers are called.
+ $popup_menu_value = param('menu_name');
=head2 CREATING AN OPTION GROUP
Named parameter style
- print $query->popup_menu(-name=>'menu_name',
+ print popup_menu(-name=>'menu_name',
-values=>[qw/eenie meenie minie/,
- $q->optgroup(-name=>'optgroup_name',
- -values ['moe','catch'],
- -attributes=>{'catch'=>{'class'=>'red'}}),
+ optgroup(-name=>'optgroup_name',
+ -values => ['moe','catch'],
+ -attributes=>{'catch'=>{'class'=>'red'}})],
-labels=>{'eenie'=>'one',
'meenie'=>'two',
'minie'=>'three'},
-default=>'meenie');
Old style
- print $query->popup_menu('menu_name',
+ print popup_menu('menu_name',
['eenie','meenie','minie',
- $q->optgroup('optgroup_name', ['moe', 'catch'],
- {'catch'=>{'class'=>'red'}})],'meenie',
+ optgroup('optgroup_name', ['moe', 'catch'],
+ {'catch'=>{'class'=>'red'}})],'meenie',
{'eenie'=>'one','meenie'=>'two','minie'=>'three'});
-optgroup creates an option group within a popup menu.
+optgroup() creates an option group within a popup menu.
=over 4
=item 5.
An optional fifth parameter (-novals) can be set to a true value and
-indicates to suppress the val attribut in each option element within
+indicates to suppress the val attribute in each option element within
the optgroup.
See the discussion on optgroup at W3C
=head2 CREATING A SCROLLING LIST
- print $query->scrolling_list('list_name',
+ print scrolling_list('list_name',
['eenie','meenie','minie','moe'],
['eenie','moe'],5,'true',{'moe'=>{'class'=>'red'}});
-or-
- print $query->scrolling_list('list_name',
+ print scrolling_list('list_name',
['eenie','meenie','minie','moe'],
['eenie','moe'],5,'true',
\%labels,%attributes);
-or-
- print $query->scrolling_list(-name=>'list_name',
+ print scrolling_list(-name=>'list_name',
-values=>['eenie','meenie','minie','moe'],
-default=>['eenie','moe'],
-size=>5,
a list under the parameter name 'list_name'. The values of the
selected items can be retrieved with:
- @selected = $query->param('list_name');
+ @selected = param('list_name');
=back
-JAVASCRIPTING: scrolling_list() recognizes the following event
-handlers: B<-onChange>, B<-onFocus>, B<-onMouseOver>, B<-onMouseOut>
-and B<-onBlur>. See textfield() for the description of when these
-handlers are called.
-
=head2 CREATING A GROUP OF RELATED CHECKBOXES
- print $query->checkbox_group(-name=>'group_name',
+ print checkbox_group(-name=>'group_name',
-values=>['eenie','meenie','minie','moe'],
-default=>['eenie','moe'],
-linebreak=>'true',
-labels=>\%labels,
-attributes=>\%attributes);
- print $query->checkbox_group('group_name',
+ print checkbox_group('group_name',
['eenie','meenie','minie','moe'],
['eenie','moe'],'true',\%labels,
{'moe'=>{'class'=>'red'}});
HTML3-COMPATIBLE BROWSERS ONLY:
- print $query->checkbox_group(-name=>'group_name',
+ print checkbox_group(-name=>'group_name',
-values=>['eenie','meenie','minie','moe'],
-rows=2,-columns=>2);
line breaks between the checkboxes so that they appear as a vertical
list. Otherwise, they will be strung together on a horizontal line.
-=item 4.
+=back
-The optional fifth argument is a pointer to an associative array
-relating the checkbox values to the user-visible labels that will
-be printed next to them (-labels). If not provided, the values will
-be used as the default.
-=item 5.
+The optional b<-labels> argument is a pointer to an associative array
+relating the checkbox values to the user-visible labels that will be
+printed next to them. If not provided, the values will be used as the
+default.
-B<HTML3-compatible browsers> (such as Netscape) can take advantage of
-the optional parameters B<-rows>, and B<-columns>. These parameters
-cause checkbox_group() to return an HTML3 compatible table containing
-the checkbox group formatted with the specified number of rows and
-columns. You can provide just the -columns parameter if you wish;
-checkbox_group will calculate the correct number of rows for you.
-=item 6.
+Modern browsers can take advantage of the optional parameters
+B<-rows>, and B<-columns>. These parameters cause checkbox_group() to
+return an HTML3 compatible table containing the checkbox group
+formatted with the specified number of rows and columns. You can
+provide just the -columns parameter if you wish; checkbox_group will
+calculate the correct number of rows for you.
-The optional sixth parameter (-attributes) is provided to assign
-any of the common HTML attributes to an individual menu item. It's
-a pointer to an associative array relating menu values to another
-associative array with the attribute's name as the key and the
-attribute's value as the value.
-To include row and column headings in the returned table, you
-can use the B<-rowheaders> and B<-colheaders> parameters. Both
-of these accept a pointer to an array of headings to use.
-The headings are just decorative. They don't reorganize the
-interpretation of the checkboxes -- they're still a single named
-unit.
+The optional B<-attributes> argument is provided to assign any of the
+common HTML attributes to an individual menu item. It's a pointer to
+an associative array relating menu values to another associative array
+with the attribute's name as the key and the attribute's value as the
+value.
-=back
+The optional B<-tabindex> argument can be used to control the order in which
+radio buttons receive focus when the user presses the tab button. If
+passed a scalar numeric value, the first element in the group will
+receive this tab index and subsequent elements will be incremented by
+one. If given a reference to an array of radio button values, then
+the indexes will be jiggered so that the order specified in the array
+will correspond to the tab order. You can also pass a reference to a
+hash in which the hash keys are the radio button values and the values
+are the tab indexes of each button. Examples:
+
+ -tabindex => 100 # this group starts at index 100 and counts up
+ -tabindex => ['moe','minie','eenie','meenie'] # tab in this order
+ -tabindex => {meenie=>100,moe=>101,minie=>102,eenie=>200} # tab in this order
When the form is processed, all checked boxes will be returned as
a list under the parameter name 'group_name'. The values of the
"on" checkboxes can be retrieved with:
- @turned_on = $query->param('group_name');
+ @turned_on = param('group_name');
The value returned by checkbox_group() is actually an array of button
elements. You can capture them and use them within tables, lists,
or in other creative ways:
- @h = $query->checkbox_group(-name=>'group_name',-values=>\@values);
+ @h = checkbox_group(-name=>'group_name',-values=>\@values);
&use_in_creative_way(@h);
-JAVASCRIPTING: checkbox_group() recognizes the B<-onClick>
-parameter. This specifies a JavaScript code fragment or
-function call to be executed every time the user clicks on
-any of the buttons in the group. You can retrieve the identity
-of the particular button clicked on using the "this" variable.
-
=head2 CREATING A STANDALONE CHECKBOX
- print $query->checkbox(-name=>'checkbox_name',
+ print checkbox(-name=>'checkbox_name',
-checked=>1,
-value=>'ON',
-label=>'CLICK ME');
-or-
- print $query->checkbox('checkbox_name','checked','ON','CLICK ME');
+ print checkbox('checkbox_name','checked','ON','CLICK ME');
checkbox() is used to create an isolated checkbox that isn't logically
related to any others.
The value of the checkbox can be retrieved using:
- $turned_on = $query->param('checkbox_name');
-
-JAVASCRIPTING: checkbox() recognizes the B<-onClick>
-parameter. See checkbox_group() for further details.
+ $turned_on = param('checkbox_name');
=head2 CREATING A RADIO BUTTON GROUP
- print $query->radio_group(-name=>'group_name',
+ print radio_group(-name=>'group_name',
-values=>['eenie','meenie','minie'],
-default=>'meenie',
-linebreak=>'true',
-or-
- print $query->radio_group('group_name',['eenie','meenie','minie'],
+ print radio_group('group_name',['eenie','meenie','minie'],
'meenie','true',\%labels,\%attributes);
HTML3-COMPATIBLE BROWSERS ONLY:
- print $query->radio_group(-name=>'group_name',
+ print radio_group(-name=>'group_name',
-values=>['eenie','meenie','minie','moe'],
-rows=2,-columns=>2);
used in the display. If not provided, the values themselves are
displayed.
-=item 6.
+=back
-B<HTML3-compatible browsers> (such as Netscape) can take advantage
-of the optional
-parameters B<-rows>, and B<-columns>. These parameters cause
-radio_group() to return an HTML3 compatible table containing
-the radio group formatted with the specified number of rows
-and columns. You can provide just the -columns parameter if you
-wish; radio_group will calculate the correct number of rows
-for you.
-=item 6.
-
-The optional sixth parameter (-attributes) is provided to assign
-any of the common HTML attributes to an individual menu item. It's
-a pointer to an associative array relating menu values to another
-associative array with the attribute's name as the key and the
-attribute's value as the value.
+All modern browsers can take advantage of the optional parameters
+B<-rows>, and B<-columns>. These parameters cause radio_group() to
+return an HTML3 compatible table containing the radio group formatted
+with the specified number of rows and columns. You can provide just
+the -columns parameter if you wish; radio_group will calculate the
+correct number of rows for you.
To include row and column headings in the returned table, you
can use the B<-rowheader> and B<-colheader> parameters. Both
interpretation of the radio buttons -- they're still a single named
unit.
-=back
+The optional B<-tabindex> argument can be used to control the order in which
+radio buttons receive focus when the user presses the tab button. If
+passed a scalar numeric value, the first element in the group will
+receive this tab index and subsequent elements will be incremented by
+one. If given a reference to an array of radio button values, then
+the indexes will be jiggered so that the order specified in the array
+will correspond to the tab order. You can also pass a reference to a
+hash in which the hash keys are the radio button values and the values
+are the tab indexes of each button. Examples:
+
+ -tabindex => 100 # this group starts at index 100 and counts up
+ -tabindex => ['moe','minie','eenie','meenie'] # tab in this order
+ -tabindex => {meenie=>100,moe=>101,minie=>102,eenie=>200} # tab in this order
+
+
+The optional B<-attributes> argument is provided to assign any of the
+common HTML attributes to an individual menu item. It's a pointer to
+an associative array relating menu values to another associative array
+with the attribute's name as the key and the attribute's value as the
+value.
When the form is processed, the selected radio button can
be retrieved using:
- $which_radio_button = $query->param('group_name');
+ $which_radio_button = param('group_name');
The value returned by radio_group() is actually an array of button
elements. You can capture them and use them within tables, lists,
or in other creative ways:
- @h = $query->radio_group(-name=>'group_name',-values=>\@values);
+ @h = radio_group(-name=>'group_name',-values=>\@values);
&use_in_creative_way(@h);
=head2 CREATING A SUBMIT BUTTON
- print $query->submit(-name=>'button_name',
+ print submit(-name=>'button_name',
-value=>'value');
-or-
- print $query->submit('button_name','value');
+ print submit('button_name','value');
submit() will create the query submission button. Every form
should have one of these.
You can figure out which button was pressed by using different
values for each one:
- $which_one = $query->param('button_name');
-
-JAVASCRIPTING: radio_group() recognizes the B<-onClick>
-parameter. See checkbox_group() for further details.
+ $which_one = param('button_name');
=head2 CREATING A RESET BUTTON
- print $query->reset
+ print reset
reset() creates the "reset" button. Note that it restores the
form to its value from the last time the script was called,
=head2 CREATING A DEFAULT BUTTON
- print $query->defaults('button_label')
+ print defaults('button_label')
defaults() creates a button that, when invoked, will cause the
form to be completely reset to its defaults, wiping out all the
=head2 CREATING A HIDDEN FIELD
- print $query->hidden(-name=>'hidden_name',
+ print hidden(-name=>'hidden_name',
-default=>['value1','value2'...]);
-or-
- print $query->hidden('hidden_name','value1','value2'...);
+ print hidden('hidden_name','value1','value2'...);
hidden() produces a text field that can't be seen by the user. It
is useful for passing state variable information from one invocation
Fetch the value of a hidden field this way:
- $hidden_value = $query->param('hidden_name');
+ $hidden_value = param('hidden_name');
Note, that just like all the other form elements, the value of a
hidden field is "sticky". If you want to replace a hidden field with
some other values after the script has been called once you'll have to
do it manually:
- $query->param('hidden_name','new','values','here');
+ param('hidden_name','new','values','here');
=head2 CREATING A CLICKABLE IMAGE BUTTON
- print $query->image_button(-name=>'button_name',
+ print image_button(-name=>'button_name',
-src=>'/source/URL',
-align=>'MIDDLE');
-or-
- print $query->image_button('button_name','/source/URL','MIDDLE');
+ print image_button('button_name','/source/URL','MIDDLE');
image_button() produces a clickable image. When it's clicked on the
position of the click is returned to your script as "button_name.x"
and "button_name.y", where "button_name" is the name you've assigned
to it.
-JAVASCRIPTING: image_button() recognizes the B<-onClick>
-parameter. See checkbox_group() for further details.
-
=over 4
=item B<Parameters:>
=back
Fetch the value of the button this way:
- $x = $query->param('button_name.x');
- $y = $query->param('button_name.y');
+ $x = param('button_name.x');
+ $y = param('button_name.y');
=head2 CREATING A JAVASCRIPT ACTION BUTTON
- print $query->button(-name=>'button_name',
+ print button(-name=>'button_name',
-value=>'user visible label',
-onClick=>"do_something()");
-or-
- print $query->button('button_name',"do_something()");
+ print button('button_name',"do_something()");
button() produces a button that is compatible with Netscape 2.0's
JavaScript. When it's pressed the fragment of JavaScript code
The interface to HTTP cookies is the B<cookie()> method:
- $cookie = $query->cookie(-name=>'sessionID',
+ $cookie = cookie(-name=>'sessionID',
-value=>'xyzzy',
-expires=>'+1h',
-path=>'/cgi-bin/database',
-domain=>'.capricorn.org',
-secure=>1);
- print $query->header(-cookie=>$cookie);
+ print header(-cookie=>$cookie);
B<cookie()> creates a new cookie. Its parameters include:
array reference, or even associative array reference. For example,
you can store an entire associative array into a cookie this way:
- $cookie=$query->cookie(-name=>'family information',
+ $cookie=cookie(-name=>'family information',
-value=>\%childrens_ages);
=item B<-path>
The cookie created by cookie() must be incorporated into the HTTP
header within the string returned by the header() method:
- print $query->header(-cookie=>$my_cookie);
+ print header(-cookie=>$my_cookie);
To create multiple cookies, give header() an array reference:
- $cookie1 = $query->cookie(-name=>'riddle_name',
+ $cookie1 = cookie(-name=>'riddle_name',
-value=>"The Sphynx's Question");
- $cookie2 = $query->cookie(-name=>'answers',
+ $cookie2 = cookie(-name=>'answers',
-value=>\%answers);
- print $query->header(-cookie=>[$cookie1,$cookie2]);
+ print header(-cookie=>[$cookie1,$cookie2]);
To retrieve a cookie, request it by name by calling cookie() method
without the B<-value> parameter:
use CGI;
$query = new CGI;
- $riddle = $query->cookie('riddle_name');
- %answers = $query->cookie('answers');
+ $riddle = cookie('riddle_name');
+ %answers = cookie('answers');
Cookies created with a single scalar value, such as the "riddle_name"
cookie, will be returned in that form. Cookies with array and hash
simple to turn a CGI parameter into a cookie, and vice-versa:
# turn a CGI parameter into a cookie
- $c=$q->cookie(-name=>'answers',-value=>[$q->param('answers')]);
+ $c=cookie(-name=>'answers',-value=>[param('answers')]);
# vice-versa
- $q->param(-name=>'answers',-value=>[$q->cookie('answers')]);
+ param(-name=>'answers',-value=>[cookie('answers')]);
See the B<cookie.cgi> example script for some ideas on how to use
cookies effectively.
You may provide a B<-target> parameter to the header() method:
- print $q->header(-target=>'ResultsWindow');
+ print header(-target=>'ResultsWindow');
This will tell the browser to load the output of your script into the
frame named "ResultsWindow". If a frame of that name doesn't already
You can specify the frame to load in the FORM tag itself. With
CGI.pm it looks like this:
- print $q->start_form(-target=>'ResultsWindow');
+ print start_form(-target=>'ResultsWindow');
When your script is reinvoked by the form, its output will be loaded
into the frame named "ResultsWindow". If one doesn't already exist
create pages in which the fill-out form and the response live in
side-by-side frames.
+=head1 SUPPORT FOR JAVASCRIPT
+
+Netscape versions 2.0 and higher incorporate an interpreted language
+called JavaScript. Internet Explorer, 3.0 and higher, supports a
+closely-related dialect called JScript. JavaScript isn't the same as
+Java, and certainly isn't at all the same as Perl, which is a great
+pity. JavaScript allows you to programmatically change the contents of
+fill-out forms, create new windows, and pop up dialog box from within
+Netscape itself. From the point of view of CGI scripting, JavaScript
+is quite useful for validating fill-out forms prior to submitting
+them.
+
+You'll need to know JavaScript in order to use it. There are many good
+sources in bookstores and on the web.
+
+The usual way to use JavaScript is to define a set of functions in a
+<SCRIPT> block inside the HTML header and then to register event
+handlers in the various elements of the page. Events include such
+things as the mouse passing over a form element, a button being
+clicked, the contents of a text field changing, or a form being
+submitted. When an event occurs that involves an element that has
+registered an event handler, its associated JavaScript code gets
+called.
+
+The elements that can register event handlers include the <BODY> of an
+HTML document, hypertext links, all the various elements of a fill-out
+form, and the form itself. There are a large number of events, and
+each applies only to the elements for which it is relevant. Here is a
+partial list:
+
+=over 4
+
+=item B<onLoad>
+
+The browser is loading the current document. Valid in:
+
+ + The HTML <BODY> section only.
+
+=item B<onUnload>
+
+The browser is closing the current page or frame. Valid for:
+
+ + The HTML <BODY> section only.
+
+=item B<onSubmit>
+
+The user has pressed the submit button of a form. This event happens
+just before the form is submitted, and your function can return a
+value of false in order to abort the submission. Valid for:
+
+ + Forms only.
+
+=item B<onClick>
+
+The mouse has clicked on an item in a fill-out form. Valid for:
+
+ + Buttons (including submit, reset, and image buttons)
+ + Checkboxes
+ + Radio buttons
+
+=item B<onChange>
+
+The user has changed the contents of a field. Valid for:
+
+ + Text fields
+ + Text areas
+ + Password fields
+ + File fields
+ + Popup Menus
+ + Scrolling lists
+
+=item B<onFocus>
+
+The user has selected a field to work with. Valid for:
+
+ + Text fields
+ + Text areas
+ + Password fields
+ + File fields
+ + Popup Menus
+ + Scrolling lists
+
+=item B<onBlur>
+
+The user has deselected a field (gone to work somewhere else). Valid
+for:
+
+ + Text fields
+ + Text areas
+ + Password fields
+ + File fields
+ + Popup Menus
+ + Scrolling lists
+
+=item B<onSelect>
+
+The user has changed the part of a text field that is selected. Valid
+for:
+
+ + Text fields
+ + Text areas
+ + Password fields
+ + File fields
+
+=item B<onMouseOver>
+
+The mouse has moved over an element.
+
+ + Text fields
+ + Text areas
+ + Password fields
+ + File fields
+ + Popup Menus
+ + Scrolling lists
+
+=item B<onMouseOut>
+
+The mouse has moved off an element.
+
+ + Text fields
+ + Text areas
+ + Password fields
+ + File fields
+ + Popup Menus
+ + Scrolling lists
+
+=back
+
+In order to register a JavaScript event handler with an HTML element,
+just use the event name as a parameter when you call the corresponding
+CGI method. For example, to have your validateAge() JavaScript code
+executed every time the textfield named "age" changes, generate the
+field like this:
+
+ print textfield(-name=>'age',-onChange=>"validateAge(this)");
+
+This example assumes that you've already declared the validateAge()
+function by incorporating it into a <SCRIPT> block. The CGI.pm
+start_html() method provides a convenient way to create this section.
+
+Similarly, you can create a form that checks itself over for
+consistency and alerts the user if some essential value is missing by
+creating it this way:
+ print startform(-onSubmit=>"validateMe(this)");
+
+See the javascript.cgi script for a demonstration of how this all
+works.
+
+
=head1 LIMITED SUPPORT FOR CASCADING STYLE SHEETS
CGI.pm has limited support for HTML3's cascading style sheets (css).
arbitrary formatting in the header, you may pass a -verbatim tag to
the -style hash, as follows:
-print $q->start_html (-STYLE => {-verbatim => '@import
+print start_html (-STYLE => {-verbatim => '@import
url("/server-common/css/'.$cssFile.'");',
-src => '/server-common/css/core.css'});
</blockquote></pre>
name/value pairs formatted nicely as a nested list. This is useful
for debugging purposes:
- print $query->Dump
+ print Dump
Produces something that looks like:
Return a list of MIME types that the remote browser accepts. If you
give this method a single argument corresponding to a MIME type, as in
-$query->Accept('text/html'), it will return a floating point value
+Accept('text/html'), it will return a floating point value
corresponding to the browser's preference for this type from 0.0
(don't want) to 1.0. Glob types (e.g. text/*) in the browser's accept
list are handled correctly.
Returns the HTTP_USER_AGENT variable. If you give
this method a single argument, it will attempt to
pattern match on it, allowing you to do something
-like $query->user_agent(netscape);
+like user_agent(netscape);
=item B<path_info()>
Returns additional path information from the script URL.
E.G. fetching /cgi-bin/your_script/additional/stuff will result in
-$query->path_info() returning "/additional/stuff".
+path_info() returning "/additional/stuff".
NOTE: The Microsoft Internet Information Server
is broken with respect to additional path information. If
For example, all three of these examples are equivalent:
- $requested_language = $q->http('Accept-language');
- $requested_language = $q->http('Accept_language');
- $requested_language = $q->http('HTTP_ACCEPT_LANGUAGE');
+ $requested_language = http('Accept-language');
+ $requested_language = http('Accept_language');
+ $requested_language = http('HTTP_ACCEPT_LANGUAGE');
=item B<https()>
in the B<header()> and B<redirect()> statements:
- print $q->header(-nph=>1);
+ print header(-nph=>1);
=back
NEW VERSION
use CGI;
- CGI::ReadParse;
+ CGI::ReadParse();
print "The value of the antique is $in{antique}.\n";
CGI.pm's ReadParse() routine creates a tied variable named %in,
this way:
$q = $in{CGI};
- print $q->textfield(-name=>'wow',
+ print textfield(-name=>'wow',
-value=>'does this really work?');
This allows you to start using the more interesting features
#!/usr/local/bin/perl
- use CGI;
-
- $query = new CGI;
+ use CGI ':standard';
- print $query->header;
- print $query->start_html("Example CGI.pm Form");
+ print header;
+ print start_html("Example CGI.pm Form");
print "<h1> Example CGI.pm Form</h1>\n";
- &print_prompt($query);
- &do_work($query);
- &print_tail;
- print $query->end_html;
+ print_prompt();
+ do_work();
+ print_tail();
+ print end_html;
sub print_prompt {
- my($query) = @_;
-
- print $query->start_form;
+ print start_form;
print "<em>What's your name?</em><br>";
- print $query->textfield('name');
- print $query->checkbox('Not my real name');
+ print textfield('name');
+ print checkbox('Not my real name');
print "<p><em>Where can you find English Sparrows?</em><br>";
- print $query->checkbox_group(
+ print checkbox_group(
-name=>'Sparrow locations',
-values=>[England,France,Spain,Asia,Hoboken],
-linebreak=>'yes',
-defaults=>[England,Asia]);
print "<p><em>How far can they fly?</em><br>",
- $query->radio_group(
+ radio_group(
-name=>'how far',
-values=>['10 ft','1 mile','10 miles','real far'],
-default=>'1 mile');
print "<p><em>What's your favorite color?</em> ";
- print $query->popup_menu(-name=>'Color',
+ print popup_menu(-name=>'Color',
-values=>['black','brown','red','yellow'],
-default=>'red');
- print $query->hidden('Reference','Monty Python and the Holy Grail');
+ print hidden('Reference','Monty Python and the Holy Grail');
print "<p><em>What have you got there?</em><br>";
- print $query->scrolling_list(
+ print scrolling_list(
-name=>'possessions',
-values=>['A Coconut','A Grail','An Icon',
'A Sword','A Ticket'],
-multiple=>'true');
print "<p><em>Any parting comments?</em><br>";
- print $query->textarea(-name=>'Comments',
+ print textarea(-name=>'Comments',
-rows=>10,
-columns=>50);
- print "<p>",$query->reset;
- print $query->submit('Action','Shout');
- print $query->submit('Action','Scream');
- print $query->endform;
+ print "<p>",reset;
+ print submit('Action','Shout');
+ print submit('Action','Scream');
+ print endform;
print "<hr>\n";
}
sub do_work {
- my($query) = @_;
my(@values,$key);
print "<h2>Here are the current settings in this form</h2>";
- foreach $key ($query->param) {
+ foreach $key (param) {
print "<strong>$key</strong> -> ";
- @values = $query->param($key);
+ @values = param($key);
print join(", ",@values),"<br>\n";
}
}