diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index e733f7b6d6..738e4681df 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Added button_to as a form-based solution to deal with harmful actions that should be hidden behind POSTs. This makes it just as easy as link_to to create a safe trigger for actions like destroy, although it's limited by being a block element, the fixed look, and a no-no inside other forms. #1371 [tom@moertel.com] + * Fixed image_tag so an exception is not thrown just because the image is missing and alt value can't be generated #1395 [Marcel] * Added a third parameter to TextHelper#auto_link called href_options for specifying additional tag options on the links generated #1401 [tyler.kovacs@gmail.com]. Example: auto_link(text, :all, { :target => "_blank" }) to have all the generated links open in a new window. diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 616f1475ec..44f5ece273 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -33,6 +33,67 @@ module ActionView end end + # Generates a form containing a sole button that submits to the + # URL given by _options_. Use this method instead of +link_to+ + # for actions that do not have the safe HTTP GET semantics + # implied by using a hypertext link. + # + # The parameters are the same as for +link_to+. Any _html_options_ + # that you pass will be applied to the inner +input+ element. + # In particular, pass + # + # :disabled => true/false + # + # as part of _html_options_ to control whether the button is + # disabled. The generated form element is given the class + # 'button-to', to which you can attach CSS styles for display + # purposes. + # + # Example 1: + # + # # inside of controller for "feeds" + # button_to "Edit", :action => 'edit', :id => 3 + # + # Generates the following HTML (sans formatting): + # + #
+ #
+ #
+ # + # Example 2: + # + # button_to "Destroy", { :action => 'destroy', :id => 3 }, + # :confirm => "Are you sure?" + # + # Generates the following HTML (sans formatting): + # + #
+ #
+ #
+ #
+ # + # *NOTE*: This method generates HTML code that represents a form. + # Forms are "block" content, which means that you should not try to + # insert them into your HTML where only inline content is expected. + # For example, you can legally insert a form inside of a +div+ or + # +td+ element or in between +p+ elements, but not in the middle of + # a run of text, nor can you place a form within another form. + # (Bottom line: Always validate your HTML before going public.) + + def button_to(name, options = {}, html_options = nil) + html_options = (html_options || {}).stringify_keys + convert_boolean_attributes!(html_options, %w( disabled )) + convert_confirm_option_to_javascript!(html_options) + url, name = options.is_a?(String) ? + [ options, name || options ] : + [ url_for(options), name || url_for(options) ] + html_options.merge!("type" => "submit", "value" => name) + "
" + + tag("input", html_options) + "
" + end + + # This tag is deprecated. Combine the link_to and AssetTagHelper::image_tag yourself instead, like: # link_to(image_tag("rss", :size => "30x45", :border => 0), "http://www.example.com") def link_image_to(src, options = {}, html_options = {}, *parameters_for_method_reference) @@ -157,6 +218,36 @@ module ActionView html_options["onclick"] = "return confirm('#{confirm.gsub(/'/, '\\\\\'')}');" end end + + # Processes the _html_options_ hash, converting the boolean + # attributes from true/false form into the form required by + # HTML/XHTML. (An attribute is considered to be boolean if + # its name is listed in the given _bool_attrs_ array.) + # + # More specifically, for each boolean attribute in _html_options_ + # given as: + # + # "attr" => bool_value + # + # if the the associated _bool_value_ evaluates to true, it is + # replaced with the attribute's name; otherwise the attribute is + # removed from the _html_options_ hash. (See the XHTML 1.0 spec, + # section 4.5 "Attribute Minimization" for more: + # http://www.w3.org/TR/xhtml1/#h-4.5) + # + # Returns the updated _html_options_ hash, which is also modified + # in place. + # + # Example: + # + # convert_boolean_attributes!( html_options, + # %w( checked disabled readonly ) ) + + def convert_boolean_attributes!(html_options, bool_attrs) + bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) } + html_options + end + end end -end \ No newline at end of file +end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index b1f2dd65e9..f208466118 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -21,6 +21,28 @@ class UrlHelperTest < Test::Unit::TestCase end # todo: missing test cases + def test_button_to_with_straight_url + assert_equal "
", button_to("Hello", "http://www.example.com") + end + + def test_button_to_with_javascript_confirm + assert_equal( + "
", + button_to("Hello", "http://www.example.com", :confirm => "Are you sure?") + ) + end + + def test_button_to_enabled_disabled + assert_equal( + "
", + button_to("Hello", "http://www.example.com", :disabled => false) + ) + assert_equal( + "
", + button_to("Hello", "http://www.example.com", :disabled => true) + ) + end + def test_link_tag_with_straight_url assert_equal "Hello", link_to("Hello", "http://www.example.com") end