The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package HTML::FormFu::Element::reCAPTCHA;

use Moose;
use MooseX::Attribute::Chained;
extends 'HTML::FormFu::Element::Multi';

use HTML::FormFu::Util qw( process_attrs _merge_hashes );
use Captcha::reCAPTCHA;
use Clone ();
use Scalar::Util qw( blessed );

has public_key  => ( is => 'rw', traits => ['Chained'] );
has private_key => ( is => 'rw', traits => ['Chained'] );

has ssl => (
    is      => 'rw',
    default => 'auto',
    lazy    => 1,
    traits  => ['Chained'],
);

has recaptcha_options => (
    is      => 'rw',
    default => sub { {} },
    lazy    => 1,
    traits  => ['Chained'],
);

after BUILD => sub {
    my $self = shift;

    $self->filename('recaptcha');
    $self->constraint_args( { type => 'reCAPTCHA' } );

    $self->constraint( $self->constraint_args );

    return;
};

sub constraint_args {
    my ( $self, $args ) = @_;

    $self->{constraint_args} ||= {};

    if ( @_ > 1 ) {
        $self->{constraint_args}
            = _merge_hashes( $self->{constraint_args}, $args, );

        my $constraint = $self->get_constraint( { type => 'reCAPTCHA' } );

        if ( defined $constraint ) {
            $constraint->populate( $self->{constraint_args} );
        }
    }

    return $self->{constraint_args};
}

sub render_data_non_recursive {
    my $self = shift;

    my $pubkey = $self->public_key || $ENV{RECAPTCHA_PUBLIC_KEY};
    my $error = undef;

    # prefer catalyst methods to %ENV vars
    my $query = $self->form->query;

    my $catalyst_compatible 
        = blessed($query)
        && $query->can('secure')
        && $query->can('address');

    my $use_ssl
        = $self->ssl eq 'auto' ? $catalyst_compatible
        : $query->secure ? $ENV{HTTPS}
        :                  $self->ssl;

    my $recaptcha_options = $self->recaptcha_options;

    my $recaptcha = Captcha::reCAPTCHA->new;

    my $recaptcha_html
        = $recaptcha->get_html( $pubkey, $error, $use_ssl, $recaptcha_options,
        );

    my $render = $self->SUPER::render_data_non_recursive( {
            recaptcha_html => $recaptcha_html,
            @_ ? %{ $_[0] } : () } );

    return $render;
}

sub string {
    my ( $self, $args ) = @_;

    $args ||= {};

    my $render
        = exists $args->{render_data}
        ? $args->{render_data}
        : $self->render_data_non_recursive;

    # field wrapper template - start

    my $html = $self->_string_field_start($render);

    # reaptcha template

    $html .= sprintf "<span%s>\n", process_attrs( $render->{attributes} );

    $html .= $render->{recaptcha_html};

    $html .= "</span>";

    # field wrapper template - end

    $html .= $self->_string_field_end($render);

    return $html;
}

sub clone {
    my $self = shift;

    my $clone = $self->SUPER::clone(@_);

    $clone->recaptcha_options( Clone::clone( $self->recaptcha_options ) );

    return $clone;
}

__PACKAGE__->meta->make_immutable;

1;

__END__

=head1 NAME

HTML::FormFu::Element::reCAPTCHA - "Are you human" tester!

=head1 SYNOPSIS

    ---
    elements:
      - type: reCAPTCHA
        name: recaptcha
        public_key: $recaptcha_net_public_key
        private_key: $recaptcha_net_private_key


=head1 DESCRIPTION

A wrapper around L<Captcha::reCAPTCHA>.
The reCAPTCHA fields aren't added to the form as "real" FormFu fields - so
the values are never available via L<params|HTML::FormFu/params>, etc.
You can check that the reCAPTCHA verified correctly, by the usual methods:
L<HTML::FormFu/submitted_and_valid> or L<HTML::FormFu/has_errors>

This element automatically adds L<HTML::FormFu::Constraint::reCAPTCHA> to
itself - you should never add it yourself.

Although this is a subclass of L<HTML::FormFu::Element::Multi>, you should
not call C<element()> or C<elements()> to try to add other fields - consider
the reCAPTCHA element a black box.

=head1 METHODS

=head2 name

Required. Although not visibly used for anything, you must give this field a
name for the L<reCAPTCHA constraint|HTML::FormFu::Constraint::reCAPTCHA> to
work correctly.

=head2 public_key

Arguments: $public_key

Required. Obtained from L<http://recaptcha.net>.

=head2 private_key

Arguments: $private_key

Required. Obtained from L<http://recaptcha.net>.

=head2 ssl

Default Value: 'auto'.

Valid Values: '1', '0' or 'auto'

Whether to load the recaptcha.net files via C<http> or C<https>.

If set to C<auto>, it will use C<https> urls if the current page is running
under ssl, otherwise it will use C<http> urls.

=head2 recaptcha_options

Arguments: \%options

See the recaptcha.net API for details of valid options.

    recaptcha_options:
      lang: de
      theme: white

=head2 constraint_args

Arguments: \%constraint_args

Options that will be passed to the
L<HTML::FormFu::Constraint::reCAPTCHA|reCAPTCHA constraint> that is
automatically added for you.

    ---
    elements:
      - type: reCAPTCHA
        name: recaptcha
        constraint_args:
          message: 'custom error message'

=head1 SEE ALSO

Is a sub-class of, and inherits methods from 
L<HTML::FormFu::Role::Element::Field>, 
L<HTML::FormFu::Element::Multi>, 
L<HTML::FormFu::Element::Block>, 
L<HTML::FormFu::Element>

L<HTML::FormFu>

=head1 AUTHOR

Carl Franks, C<cfranks@cpan.org>

=head1 LICENSE

This library is free software, you can redistribute it and/or modify it under
the same terms as Perl itself.

=cut