HTML-FormsDj

 view release on metacpan or  search on metacpan

lib/HTML/FormsDj.pm  view on Meta::CPAN

    return qq();
  }
}

sub _check_csrf {
  my ($this, %data) = @_;

  if (! exists $data{csrftoken}) {
    $this->{error}   = 'CSRF ERROR: CSRF token is not supplied with POST data!';
    return 0;
  }

  if (! exists $this->{'_csrf_cookie'}) {
    $this->{error}   = 'CSRF ERROR: CSRF cookie is not set correctly(notexist)!';
    return 0;
  }
  else {
    if(! $this->{'_csrf_cookie'} ) {
      $this->{error}   = 'CSRF ERROR: CSRF cookie is not set correctly(undef)!';
      return 0;
    }
  }

  my $posttoken   = $data{csrftoken};          # hidden post var
  my $cookietoken = $this->{'_csrf_cookie'};   # cookie

  if ($posttoken ne $cookietoken) {
    $this->{error}   = 'CSRF ERROR:  supplied COOKIE csrftoken doesnt match stored csrf token!';
    $this->{error}  .= sprintf "<br>post: %s<br>cookie: %s", $posttoken, $cookietoken;
    return 0;
  }

  return 1;
}

sub as_p {
  my($this) = @_;
  my $html;
  $this->_normalize();

  if ($this->{csrf}) {
    $html = $this->csrftoken();
  }

  if (exists $this->{meta}->{fields}) {
    # just an array of fields
    foreach my $field( @{$this->{meta}->{fields}}) {
      $html .= $this->_p_field($field);
    }
  }
  else {
    # it's a fieldset
    foreach my $fieldset (@{$this->{meta}->{fieldsets}}) {
      my $htmlfields;
      foreach my $field (@{$fieldset->{fields}}) {
	$htmlfields .= $this->_p_field($field);
      }
      $html .= $this->_fieldset(
				join(' ', @{$fieldset->{classes}}),
				$fieldset->{id},
				$fieldset->{legend},
				$htmlfields
				);
    }
  }

  return $html;
}

sub as_table {
  my($this) = @_;
  my $html;
  $this->_normalize();

  if ($this->{csrf}) {
    $html = $this->csrftoken();
  }

  if (exists $this->{meta}->{fields}) {
    # just an array of fields
    foreach my $field( @{$this->{meta}->{fields}}) {
      $html .= $this->_tr_field($field);
    }
    return $this->_table('formtable', $html);
  }
  else {
    # it's a fieldset
    foreach my $fieldset (@{$this->{meta}->{fieldsets}}) {
      my $htmlfields;
      foreach my $field (@{$fieldset->{fields}}) {
	$htmlfields .= $this->_tr_field($field);
      }
      $html .= $this->_table($fieldset->{id}, $htmlfields, $fieldset->{legend});
    }
  }

  return $html;
}

sub as_is {
  my($this) = @_;
  $this->_normalize();
  return $this->{meta};
}

sub fields {
  my($this) = @_;
  if (exists $this->{meta}->{fields}) {
    return @{$this->{meta}->{fields}};
  }
  else {
    return ();
  }
}

sub fieldsets {
  my($this) = @_;
  if (exists $this->{meta}->{fieldsets}) {
    return @{ $this->{meta}->{fieldsets} };
  }
  else {
    return ();
  }
}

sub dumpmeta {
  my($this) = @_;
  my $dump = Dumper($this->{meta});
  $dump =~ s/^\$VAR1 = /        /;
  return sprintf qq(<pre>%s</pre>), $dump;
}

sub csrftoken {
  my($this) = @_;
  if ($this->{csrf}) {
    return sprintf qq(<input type="hidden" name="csrftoken" value="%s"/>), $_csrftoken;
  }
  else {
    return qq();
  }
}

sub getcsrf {
  my($this) = @_;
  if ($this->{csrf}) {
    return $_csrftoken;
  }
  else {
    return qq();
  }
}

sub csrfcookie {
  my($this, $token) = @_;
  if ($this->{csrf}) {
    $this->{'_csrf_cookie'} = $token;
  }
  return 1;
}

#
# INTERNALS HERE
#

sub _message {
  my($this, $message, $id) = @_;
  return sprintf qq(<span class="fielderror" id="%s">%s</span>), $id, $message;
}

sub _tr_field {
  my($this, $field) = @_;
  return $this->_tr(
		   join(q( ), @{$field->{classes}}),
		   $field->{id},
		   $this->_label(
				 $field->{id} . '_input',
				 $field->{label}
				),
		   $this->_input(
				 $field->{id} . '_input',
				 $field->{type},
				 $field->{field},
				 $field->{value},
				 $field->{default} # hashref, arrayref or scalar
				) .
		    $this->_message($field->{message}, $field->{id} . '_message')
		  );
}

sub _tr {
  my($this, $class, $id, $label, $input) = @_;
  return sprintf qq(<tr id="%s"><td class="%s tdlabel">%s</td><td class="%s tdinput">%s</td></tr>\n),
    $id, $class, $label, $class, $input;
}

sub _table {
  my($this, $id, $cdata, $legend) = @_;
  my $html = sprintf qq(<table id="%s">), $id;
  if ($legend) {
    $html .= sprintf qq(<thead><tr><td colspan="2">%s</td></tr></thead>\n), $legend;
  }
  $html .= sprintf qq(<tbody>%s</tbody></table>\n), $cdata;
  return $html;
}

sub _normalize_field {
  my($this, $field) = @_;

  if (! exists $field->{label}) {
    $field->{label} = $field->{field};
    $field->{label} =~ s/^(.)/uc($1)/e;
  }

  if (exists $this->{markrequired} && $this->{field}->{$field->{field}}->{required}) {
    if ($this->{markrequired} eq 'asterisk') {
      $field->{label} = $field->{label} . ' *';
    }
    elsif ($this->{markrequired} eq 'bold') {
      $field->{label} = $this->_b($field->{label});
    }
    else {
      $field->{label} = $field->{label} . $this->{markrequired};
    }
  }

  if (! exists $field->{classes}) {
    $field->{classes} = [ qw(formfield) ];
  }

  if (! exists $field->{id}) {
    $field->{id} = 'id_formfield_' . $field->{field};
  }

  if (! exists  $field->{message}) {
    $field->{message} = qq();
  }
  if (exists $this->{invalid}->{$field->{field}}) {
    if (! exists $field->{message}) {
      $field->{message} = 'invalid input';
    }
    $field->{error} = $this->{invalid}->{$field->{field}};
  }

  if (exists $this->{missing}->{$field->{field}}) {
    if (! exists $field->{message}) {
      $field->{message} = 'missing input';
    }
    $field->{error} = 'missing input';
  }

  if (! exists $this->{raw}->{$field->{field}}) {
    $field->{value} = qq();
  }
  else {
    $field->{value} = $this->{raw}->{$field->{field}};
  }

  if (! exists $this->{field}->{$field->{field}}->{type}) {
    $field->{type} = 'text';
  }
  else {
    $field->{type} = $this->{field}->{$field->{field}}->{type};
  }

  if (! exists $field->{default}) {
    $field->{default} = qq();
  }

  return $field;
}

sub _normalize {
  my($this) = @_;

  if (exists $this->{meta}->{fields}) {
    my @normalized;
    foreach my $field( @{$this->{meta}->{fields}}) {
      if (! exists $field->{field}) {
	carp 'unnamed field, ignoring!';
	next;
      }

      push @normalized, $this->_normalize_field($field);
    }
    $this->{meta}->{fields} = \@normalized;
  }

  if (exists $this->{meta}->{fieldsets}) {

    my @fieldsets;
    foreach my $fieldset (@{$this->{meta}->{fieldsets}}) {
      if (! exists $fieldset->{id}) {
	if (! exists $fieldset->{name}) {
	  $fieldset->{id} = 'id_fieldset_' . $.;
	}
	else {
	  $fieldset->{id} = 'id_fieldset_' . $fieldset->{name};
	}
      }

      if (! exists $fieldset->{classes}) {
	$fieldset->{classes} = [ qw(formfieldset) ];
      }

      if (! exists $fieldset->{legend}) {
	$fieldset->{legend} = qq();
      }

      my @normalized;
      foreach my $field (@{$fieldset->{fields}}) {
	if (! exists $field->{field}) {
	  carp 'unnamed field, ignoring!';
	  next;
	}
	push @normalized, $this->_normalize_field($field);
      }

      $fieldset->{fields} = \@normalized;
      push @fieldsets, $fieldset;
    }
    $this->{meta}->{fieldsets} = \@fieldsets;
  }

  return;
}


sub _fieldset {
  my($this, $class, $id, $legend, $cdata) = @_;
  return sprintf qq(<fieldset class="%s" id="%s"><legend>%s</legend>\n%s\n</fieldset>\n),
    $class, $id, $legend, $cdata;
}

sub _p_field {
  my($this, $field) = @_;
  return $this->_p(
		   join(' ', @{$field->{classes}}),
		   $field->{id},
		   $this->_label(
				 $field->{id} . '_input',
				 $field->{label}
				) .
		   $this->_input(
				 $field->{id} . '_input',
				 $field->{type},
				 $field->{field},
				 $field->{value},
				 $field->{default} # hashref, arrayref or scalar
				) .
		   $this->_message($field->{message}, $field->{id} . '_message')
		  );
}

sub _p {
  my ($this, $class, $id, $cdata) = @_;
  return sprintf qq(<p class="%s" id="%s">%s</p>\n), $class, $id, $cdata;
}

sub _label {
  my ($this, $id, $name) = @_;
  return sprintf qq(\n  <label for="%s">%s</label>), $id, $name;
}

sub _input {
  my ($this, $id, $type, $name, $value, $default) = @_;

  my $html;

  if ($type eq 'text' || $type eq 'password') {
    if (! $value) {
      $value = $default;
    }
    $html = sprintf qq(\n  <input type="%s" id="%s" name="%s" value="%s"/>\n), $type, $id, $name, $value;
  }
  elsif ($type eq 'choice') {
    my $html = sprintf qq(\n  <select name="%s" id="%s">), $name, $id;
    if (ref($default) eq 'HASH') {
      foreach my $option (sort keys %{$default}) {
	$html .= sprintf qq(\n    <option value="%s">%s</option>), $option, $default->{$option};
      }
    }
    elsif (ref($default) eq 'ARRAY') {
      foreach my $option (@{$default}) {
	my $selected = qq();
	if ($value eq $option->{value}) {
	  $selected = ' selected';
	}
	$html .= sprintf qq(\n    <option value="%s"%s>%s</option>), $option->{value}, $selected, $option->{label};
      }
    }
    $html .= qq(\n  </select>\n);

lib/HTML/FormsDj.pm  view on Meta::CPAN


All other parameters are optional. If you omit them, or if you omit
the whole meta parameter, B<HTML::FormsDj> will generate it itself
using reasonable defaults based on the variable names.

Parameters of a field hash are:

=over

=item B<field>

As mentioned above, the name of the form variable.

=item B<label>

A label which will be put before the input field.

=item B<message>

A message, which will be shown if there are some
errors or if the field were missing.

=item B<classes>

A list (arrayref) of CSS class names to apply to the
field.

=item B<id>

A CSS id you may assign to the field.

=back

=head2 META FIELDSET

Sometimes a plain list of fields may not be sufficient, especially
if you have to render a large input form. You may use a B<fieldset>
instead of a B<field> to better organize the display of the form.

Again, using the example used above, you could write:

   my $form = new HTML::FormsDj(
      field => {
		title   => {
			    type     => 'text',
			    validate => valid_string(),
			    required => 1,
			},
		author  => {
			    type     => 'text',
			    validate => valid_string(),
			    required => 1,
			   },
	       },
      name => 'registerform',
      meta => {
                 fieldsets => [
                                {
                                  name        => 'titleset',
                                  description => 'Enter book title data here',
                                  legend      => 'Book Title',
                                  fields      => [
                                                   {
                                                    field    => 'title',
                                                    label    => 'Enter a book title',
                                                    message  => 'A book title must be at least 4 characters long',
                                                    classes  => [ qw(titlefield) ],
                                                   },
                                                  ]
                                },
                                {
                                  name        => 'authorset',
                                  description => 'Enter book author data here',
                                  legend      => 'Book Author',
                                  fields      => [
                                                   {
                                                    field    => 'author',
                                                    label    => 'Enter an author name',
                                                    message  => 'A book title must be at least 4 characters long',
                                                    classes  => [ qw(authorfield) ],
                                                   },
                                                  ]
                                },
                              ]
      }
   );

Ok, this looks a little bit more complicated. Essentially
there is just one more level in the definition. A fieldset
is just a list of groups of fields. It is defined as a list
(an arrayref) which contains hashes, one hash per fieldset.

Each fieldset hash consists of some parameters, like a B<name>
or a B<legend> plus a list of fields, which is exactly defined
as in the B<meta> parameter B<fields> as seen above.

The output of the form is just devided into fieldsets, which
is a HTML tag as well. Each fieldset will have a title, the B<legend>
parameter, an (optional) B<description> and a B<name>.

This is the very same as the META subclass in django forms
is working.

B<Please note: you cannot mix a field list and fieldsets!>

Only one of the two is possible.

If you omit the B<meta> parameter at all, B<HTML::FormsDj> will
always generate a plain field list.

=head2 ADDING DEFAULT VALUES

IN some cases you'll need to put some defaults for form
variables, eg. for choices or options.

You can do this by adding a B<default> parameter to
the field definition in your meta hash.

For text type variables this can just be a scalar. For choices
and options you can supply a hash- or an array reference.

An example for a choice:

  # other fields
  ,
  {
    field => 'redirect',
    label => 'Redirect to page',
    default => [
                   {
                    value => 1,
                    label => '/home'
                   },
                   {
                    value => 2,
                    label => '/profile'
                   }
                ],
  }
  ,
  # other fields

In this example we've a choice which contains two values
for the generated select form tag. Here we've used an array,
which is the preferred way since this preserves order.

However, you might also supply a hash:

  # other fields
  ,
  {
    field => 'redirect',
    label => 'Redirect to page',
    default => {
                 1 => '/home',
                 2 => '/profile'
                }
  }



( run in 0.625 second using v1.01-cache-2.11-cpan-e1769b4cff6 )