Skip to content

Commit

Permalink
add button_to and csrf_button_to helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Dec 16, 2016
1 parent ae2a696 commit d0480f9
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 2 deletions.
4 changes: 3 additions & 1 deletion Changes
@@ -1,5 +1,7 @@

7.12 2016-12-09
7.12 2016-12-17
- Added button_to and csrf_button_to helpers to
Mojolicious::Plugin::TagHelpers.

7.11 2016-11-30
- Added EXPERIMENTAL close_idle_connections method to Mojo::Server::Daemon.
Expand Down
42 changes: 42 additions & 0 deletions lib/Mojolicious/Plugin/TagHelpers.pm
Expand Up @@ -21,7 +21,9 @@ sub register {
);
$app->helper($_ => __PACKAGE__->can("_$_")) for @helpers;

$app->helper(button_to => sub { _button_to(0, @_) });
$app->helper(check_box => sub { _input(@_, type => 'checkbox') });
$app->helper(csrf_button_to => sub { _button_to(1, @_) });
$app->helper(file_field => sub { _empty_field('file', @_) });
$app->helper(image => sub { _tag('img', src => shift->url_for(shift), @_) });
$app->helper(input_tag => sub { _input(@_) });
Expand All @@ -32,6 +34,12 @@ sub register {
$app->helper($_ => sub { shift; _tag(@_) }) for qw(t tag);
}

sub _button_to {
my ($csrf, $c, $text) = (shift, shift, shift);
my $prefix = $csrf ? _csrf_field($c) : '';
return _form_for($c, @_, sub { $prefix . _submit_button($c, $text) });
}

sub _csrf_field {
my $c = shift;
return _hidden_field($c, csrf_token => $c->helpers->csrf_token, @_);
Expand Down Expand Up @@ -245,6 +253,28 @@ by default.
L<Mojolicious::Plugin::TagHelpers> implements the following helpers.
=head2 button_to
%= button_to Test => 'some_get_route'
%= button_to Test => some_get_route => {id => 23} => (class => 'menu')
%= button_to Test => 'http://example.com/test' => (class => 'menu')
%= button_to Remove => 'some_delete_route'
Generate portable C<form> tag with L</"form_for">, containing a single button.
<form action="/path/to/get/route">
<input type="submit" value="Test">
</form>
<form action="/path/to/get/route/23" class="menu">
<input type="submit" value="Test">
</form>
<form action="http://example.com/test" class="menu">
<input type="submit" value="Test">
</form>
<form action="/path/to/delete/route?_method=DELETE" method="POST">
<input type="submit" value="Remove">
</form>
=head2 check_box
%= check_box 'employed'
Expand All @@ -271,6 +301,18 @@ automatically get picked up and shown as default.
<input name="background" type="color" value="#ffffff">
<input id="foo" name="background" type="color" value="#ffffff">
=head2 csrf_button_to
%= csrf_button_to 'Remove' => 'some_delete_route'
Same as L</"button_to">, but also includes a field generated with
L</"csrf_field">.
<form action="/path/to/delete/route?_method=DELETE" method="POST">
<input name="csrf_token" type="hidden" value="fa6a08...">
<input type="submit" value="Remove">
</form>
=head2 csrf_field
%= csrf_field
Expand Down
27 changes: 27 additions & 0 deletions t/mojolicious/tag_helper_lite_app.t
Expand Up @@ -16,6 +16,8 @@ get 'tags_with_error';

any [qw(GET POST)] => 'links';

get '/buttons';

get 'script';

get 'style';
Expand Down Expand Up @@ -110,6 +112,24 @@ $t->post_ok('/links')->status_is(200)->content_is(<<'EOF');
<a href="/form/23" title="Foo">Foo</a>
EOF

# Buttons
$t->get_ok('/buttons')->status_is(200)
->content_is('<form action="/links">'
. '<input type="submit" value="First test">'
. "</form>\n"
. '<form action="/links.txt">'
. '<input type="submit" value="Second">'
. "</form>\n"
. '<form action="/" class="menu">'
. '<input type="submit" value="Third">'
. "</form>\n"
. '<form action="http://example.com">'
. '<input type="submit" value="Fourth">'
. "</form>\n"
. '<form action="/selection?_method=PUT" method="POST">'
. '<input type="submit" value="Fifth">'
. "</form>\n");

# Scripts
$t->get_ok('/script')->status_is(200)->content_is(<<EOF);
<script src="/script.js"></script>
Expand Down Expand Up @@ -547,6 +567,13 @@ __DATA__
<%= link_to Baz => '#baz' %>
<%= link_to form => {test => 23} => (title => 'Foo') => begin %>Foo<% end %>
@@ buttons.html.ep
%= button_to 'First test' => 'links'
%= button_to 'Second' => 'links' => {format => 'txt'}
%= button_to 'Third' => '/' => (class => 'menu')
%= button_to 'Fourth' => 'http://example.com'
%= button_to 'Fifth' => 'selection'
@@ script.html.ep
<%= javascript '/script.js' %>
<%= javascript begin %>
Expand Down
7 changes: 6 additions & 1 deletion t/mojolicious/validation_lite_app.t
Expand Up @@ -316,7 +316,11 @@ $t->get_ok('/forgery' => form => {foo => 'bar'})->status_is(200)
my $token = $t->ua->get('/forgery')->res->dom->at('[name=csrf_token]')->val;
$t->post_ok('/forgery' => form => {csrf_token => $token, foo => 'bar'})
->status_is(200)->content_unlike(qr/Wrong or missing CSRF token!/)
->element_exists('[value=bar]')->element_exists_not('.field-with-error');
->element_exists('[value=bar]')->element_exists_not('.field-with-error')
->element_count_is('[name=csrf_token]', 2)->element_count_is('form', 2)
->element_exists('form > input[name=csrf_token] + input[type=submit]');
is $t->tx->res->dom->find('[name=csrf_token]')->[0]->val,
$t->tx->res->dom->find('[name=csrf_token]')->[1]->val, 'same token';

# Correct CSRF token (header)
$t->post_ok('/forgery' => {'X-CSRF-Token' => $token} => form => {foo => 'bar'})
Expand Down Expand Up @@ -391,3 +395,4 @@ __DATA__
%= csrf_field
%= text_field 'foo'
%= end
%= csrf_button_to Root => '/'

0 comments on commit d0480f9

Please sign in to comment.