actionview 6.1.7.2 → 7.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -277
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +3 -3
  5. data/app/assets/javascripts/rails-ujs.esm.js +686 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +37 -19
  8. data/lib/action_view/buffers.rb +107 -9
  9. data/lib/action_view/cache_expiry.rb +48 -37
  10. data/lib/action_view/context.rb +1 -1
  11. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  12. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  13. data/lib/action_view/dependency_tracker.rb +6 -147
  14. data/lib/action_view/deprecator.rb +7 -0
  15. data/lib/action_view/digestor.rb +8 -5
  16. data/lib/action_view/flows.rb +4 -4
  17. data/lib/action_view/gem_version.rb +4 -4
  18. data/lib/action_view/helpers/active_model_helper.rb +3 -3
  19. data/lib/action_view/helpers/asset_tag_helper.rb +200 -60
  20. data/lib/action_view/helpers/asset_url_helper.rb +22 -21
  21. data/lib/action_view/helpers/atom_feed_helper.rb +8 -9
  22. data/lib/action_view/helpers/cache_helper.rb +55 -12
  23. data/lib/action_view/helpers/capture_helper.rb +34 -14
  24. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  25. data/lib/action_view/helpers/controller_helper.rb +8 -2
  26. data/lib/action_view/helpers/csp_helper.rb +3 -3
  27. data/lib/action_view/helpers/csrf_helper.rb +4 -4
  28. data/lib/action_view/helpers/date_helper.rb +123 -57
  29. data/lib/action_view/helpers/debug_helper.rb +6 -4
  30. data/lib/action_view/helpers/form_helper.rb +253 -97
  31. data/lib/action_view/helpers/form_options_helper.rb +72 -34
  32. data/lib/action_view/helpers/form_tag_helper.rb +189 -58
  33. data/lib/action_view/helpers/javascript_helper.rb +4 -5
  34. data/lib/action_view/helpers/number_helper.rb +43 -335
  35. data/lib/action_view/helpers/output_safety_helper.rb +6 -6
  36. data/lib/action_view/helpers/rendering_helper.rb +6 -7
  37. data/lib/action_view/helpers/sanitize_helper.rb +54 -24
  38. data/lib/action_view/helpers/tag_helper.rb +42 -35
  39. data/lib/action_view/helpers/tags/base.rb +16 -77
  40. data/lib/action_view/helpers/tags/check_box.rb +1 -1
  41. data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
  42. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
  43. data/lib/action_view/helpers/tags/collection_select.rb +4 -1
  44. data/lib/action_view/helpers/tags/date_field.rb +1 -1
  45. data/lib/action_view/helpers/tags/date_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
  47. data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
  48. data/lib/action_view/helpers/tags/file_field.rb +16 -0
  49. data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
  50. data/lib/action_view/helpers/tags/month_field.rb +1 -1
  51. data/lib/action_view/helpers/tags/select.rb +4 -1
  52. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  53. data/lib/action_view/helpers/tags/time_field.rb +11 -2
  54. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
  55. data/lib/action_view/helpers/tags/week_field.rb +1 -1
  56. data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
  57. data/lib/action_view/helpers/tags.rb +5 -2
  58. data/lib/action_view/helpers/text_helper.rb +180 -97
  59. data/lib/action_view/helpers/translation_helper.rb +14 -45
  60. data/lib/action_view/helpers/url_helper.rb +230 -132
  61. data/lib/action_view/helpers.rb +27 -25
  62. data/lib/action_view/layouts.rb +15 -10
  63. data/lib/action_view/log_subscriber.rb +49 -32
  64. data/lib/action_view/lookup_context.rb +58 -61
  65. data/lib/action_view/model_naming.rb +2 -2
  66. data/lib/action_view/path_registry.rb +57 -0
  67. data/lib/action_view/path_set.rb +28 -35
  68. data/lib/action_view/railtie.rb +44 -9
  69. data/lib/action_view/record_identifier.rb +16 -9
  70. data/lib/action_view/render_parser.rb +188 -0
  71. data/lib/action_view/renderer/abstract_renderer.rb +3 -3
  72. data/lib/action_view/renderer/collection_renderer.rb +10 -2
  73. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
  74. data/lib/action_view/renderer/partial_renderer.rb +3 -36
  75. data/lib/action_view/renderer/renderer.rb +6 -4
  76. data/lib/action_view/renderer/streaming_template_renderer.rb +6 -5
  77. data/lib/action_view/renderer/template_renderer.rb +9 -4
  78. data/lib/action_view/rendering.rb +25 -7
  79. data/lib/action_view/ripper_ast_parser.rb +198 -0
  80. data/lib/action_view/routing_url_for.rb +8 -5
  81. data/lib/action_view/template/error.rb +122 -14
  82. data/lib/action_view/template/handlers/builder.rb +4 -4
  83. data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
  84. data/lib/action_view/template/handlers/erb.rb +79 -1
  85. data/lib/action_view/template/handlers.rb +4 -4
  86. data/lib/action_view/template/html.rb +4 -4
  87. data/lib/action_view/template/inline.rb +3 -3
  88. data/lib/action_view/template/raw_file.rb +4 -4
  89. data/lib/action_view/template/renderable.rb +1 -1
  90. data/lib/action_view/template/resolver.rb +96 -313
  91. data/lib/action_view/template/text.rb +4 -4
  92. data/lib/action_view/template/types.rb +25 -32
  93. data/lib/action_view/template.rb +245 -41
  94. data/lib/action_view/template_details.rb +66 -0
  95. data/lib/action_view/template_path.rb +66 -0
  96. data/lib/action_view/test_case.rb +182 -23
  97. data/lib/action_view/testing/resolvers.rb +11 -12
  98. data/lib/action_view/unbound_template.rb +43 -7
  99. data/lib/action_view/version.rb +1 -1
  100. data/lib/action_view/view_paths.rb +19 -28
  101. data/lib/action_view.rb +6 -4
  102. data/lib/assets/compiled/rails-ujs.js +36 -5
  103. metadata +32 -25
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "action_view/helpers/javascript_helper"
4
3
  require "active_support/core_ext/array/access"
5
4
  require "active_support/core_ext/hash/keys"
6
5
  require "active_support/core_ext/string/output_safety"
6
+ require "action_view/helpers/content_exfiltration_prevention_helper"
7
+ require "action_view/helpers/tag_helper"
7
8
 
8
9
  module ActionView
9
- # = Action View URL Helpers
10
- module Helpers #:nodoc:
10
+ module Helpers # :nodoc:
11
+ # = Action View URL \Helpers
12
+ #
11
13
  # Provides a set of methods for making links and getting URLs that
12
14
  # depend on the routing subsystem (see ActionDispatch::Routing).
13
15
  # This allows you to use the same format for links in views
@@ -22,6 +24,7 @@ module ActionView
22
24
  extend ActiveSupport::Concern
23
25
 
24
26
  include TagHelper
27
+ include ContentExfiltrationPreventionHelper
25
28
 
26
29
  module ClassMethods
27
30
  def _url_for_modules
@@ -29,6 +32,8 @@ module ActionView
29
32
  end
30
33
  end
31
34
 
35
+ mattr_accessor :button_to_generates_button_tag, default: false
36
+
32
37
  # Basic implementation of url_for to allow use helpers without routes existence
33
38
  def url_for(options = nil) # :nodoc:
34
39
  case options
@@ -83,37 +88,15 @@ module ActionView
83
88
  # # name
84
89
  # end
85
90
  #
91
+ # link_to(active_record_model)
92
+ #
86
93
  # ==== Options
87
94
  # * <tt>:data</tt> - This option can be used to add custom data attributes.
88
- # * <tt>method: symbol of HTTP verb</tt> - This modifier will dynamically
89
- # create an HTML form and immediately submit the form for processing using
90
- # the HTTP verb specified. Useful for having links perform a POST operation
91
- # in dangerous actions like deleting a record (which search bots can follow
92
- # while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>.
93
- # Note that if the user has JavaScript disabled, the request will fall back
94
- # to using GET. If <tt>href: '#'</tt> is used and the user has JavaScript
95
- # disabled clicking the link will have no effect. If you are relying on the
96
- # POST behavior, you should check for it in your controller's action by using
97
- # the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>patch?</tt>, or <tt>put?</tt>.
98
- # * <tt>remote: true</tt> - This will allow the unobtrusive JavaScript
99
- # driver to make an Ajax request to the URL in question instead of following
100
- # the link. The drivers each provide mechanisms for listening for the
101
- # completion of the Ajax request and performing JavaScript operations once
102
- # they're complete
103
- #
104
- # ==== Data attributes
105
- #
106
- # * <tt>confirm: 'question?'</tt> - This will allow the unobtrusive JavaScript
107
- # driver to prompt with the question specified (in this case, the
108
- # resulting text would be <tt>question?</tt>. If the user accepts, the
109
- # link is processed normally, otherwise no action is taken.
110
- # * <tt>:disable_with</tt> - Value of this parameter will be used as the
111
- # name for a disabled version of the link. This feature is provided by
112
- # the unobtrusive JavaScript driver.
113
95
  #
114
96
  # ==== Examples
97
+ #
115
98
  # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
116
- # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
99
+ # and newer RESTful routes. Current \Rails style favors RESTful routes whenever possible, so base
117
100
  # your application on resources and use
118
101
  #
119
102
  # link_to "Profile", profile_path(@profile)
@@ -144,6 +127,12 @@ module ActionView
144
127
  # link_to nil, "http://example.com"
145
128
  # # => <a href="http://www.example.com">http://www.example.com</a>
146
129
  #
130
+ # More concise yet, when +name+ is an Active Record model that defines a
131
+ # +to_s+ method returning a default value or a model instance attribute
132
+ #
133
+ # link_to @profile
134
+ # # => <a href="http://www.example.com/profiles/1">Eileen</a>
135
+ #
147
136
  # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
148
137
  #
149
138
  # <%= link_to(@profile) do %>
@@ -179,27 +168,76 @@ module ActionView
179
168
  # link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
180
169
  # # => <a href="/searches?foo=bar&baz=quux">Nonsense search</a>
181
170
  #
182
- # The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
171
+ # You can set any link attributes such as <tt>target</tt>, <tt>rel</tt>, <tt>type</tt>:
172
+ #
173
+ # link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
174
+ # # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
175
+ #
176
+ # ==== Turbo
177
+ #
178
+ # Rails 7 ships with Turbo enabled by default. Turbo provides the following +:data+ options:
179
+ #
180
+ # * <tt>turbo_method: symbol of HTTP verb</tt> - Performs a Turbo link visit
181
+ # with the given HTTP verb. Forms are recommended when performing non-+GET+ requests.
182
+ # Only use <tt>data-turbo-method</tt> where a form is not possible.
183
+ #
184
+ # * <tt>turbo_confirm: "question?"</tt> - Adds a confirmation dialog to the link with the
185
+ # given value.
183
186
  #
184
- # link_to("Destroy", "http://www.example.com", method: :delete)
185
- # # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
187
+ # {Consult the Turbo Handbook for more information on the options
188
+ # above.}[https://turbo.hotwired.dev/handbook/drive#performing-visits-with-a-different-method]
186
189
  #
187
- # You can also use custom data attributes using the <tt>:data</tt> option:
190
+ # ===== \Examples
191
+ #
192
+ # link_to "Delete profile", @profile, data: { turbo_method: :delete }
193
+ # # => <a href="/profiles/1" data-turbo-method="delete">Delete profile</a>
194
+ #
195
+ # link_to "Visit Other Site", "https://rubyonrails.org/", data: { turbo_confirm: "Are you sure?" }
196
+ # # => <a href="https://rubyonrails.org/" data-turbo-confirm="Are you sure?">Visit Other Site</a>
197
+ #
198
+ # ==== Deprecated: \Rails UJS Attributes
199
+ #
200
+ # Prior to \Rails 7, \Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following \Rails 7,
201
+ # this library is no longer on by default. This library integrated with the following options:
202
+ #
203
+ # * <tt>method: symbol of HTTP verb</tt> - This modifier will dynamically
204
+ # create an HTML form and immediately submit the form for processing using
205
+ # the HTTP verb specified. Useful for having links perform a POST operation
206
+ # in dangerous actions like deleting a record (which search bots can follow
207
+ # while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>.
208
+ # Note that if the user has JavaScript disabled, the request will fall back
209
+ # to using GET. If <tt>href: '#'</tt> is used and the user has JavaScript
210
+ # disabled clicking the link will have no effect. If you are relying on the
211
+ # POST behavior, you should check for it in your controller's action by using
212
+ # the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>patch?</tt>, or <tt>put?</tt>.
213
+ # * <tt>remote: true</tt> - This will allow <tt>@rails/ujs</tt>
214
+ # to make an Ajax request to the URL in question instead of following
215
+ # the link.
216
+ #
217
+ # <tt>@rails/ujs</tt> also integrated with the following +:data+ options:
218
+ #
219
+ # * <tt>confirm: "question?"</tt> - This will allow <tt>@rails/ujs</tt>
220
+ # to prompt with the question specified (in this case, the
221
+ # resulting text would be <tt>question?</tt>). If the user accepts, the
222
+ # link is processed normally, otherwise no action is taken.
223
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the
224
+ # name for a disabled version of the link.
225
+ #
226
+ # ===== \Rails UJS Examples
227
+ #
228
+ # link_to "Remove Profile", profile_path(@profile), method: :delete
229
+ # # => <a href="/profiles/1" rel="nofollow" data-method="delete">Remove Profile</a>
188
230
  #
189
231
  # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
190
232
  # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
191
233
  #
192
- # Also you can set any link attributes such as <tt>target</tt>, <tt>rel</tt>, <tt>type</tt>:
193
- #
194
- # link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
195
- # # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
196
234
  def link_to(name = nil, options = nil, html_options = nil, &block)
197
235
  html_options, options, name = options, name, block if block_given?
198
236
  options ||= {}
199
237
 
200
238
  html_options = convert_options_to_data_attributes(options, html_options)
201
239
 
202
- url = url_for(options)
240
+ url = url_target(name, options)
203
241
  html_options["href"] ||= url
204
242
 
205
243
  content_tag("a", name || url, html_options, &block)
@@ -208,54 +246,64 @@ module ActionView
208
246
  # Generates a form containing a single button that submits to the URL created
209
247
  # by the set of +options+. This is the safest method to ensure links that
210
248
  # cause changes to your data are not triggered by search bots or accelerators.
211
- # If the HTML button does not work with your layout, you can also consider
212
- # using the +link_to+ method with the <tt>:method</tt> modifier as described in
213
- # the +link_to+ documentation.
214
- #
215
- # By default, the generated form element has a class name of <tt>button_to</tt>
216
- # to allow styling of the form itself and its children. This can be changed
217
- # using the <tt>:form_class</tt> modifier within +html_options+. You can control
218
- # the form submission and input element behavior using +html_options+.
219
- # This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
220
- # If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
221
- # You can also disable the button by passing <tt>disabled: true</tt> in +html_options+.
222
- # If you are using RESTful routes, you can pass the <tt>:method</tt>
223
- # to change the HTTP verb used to submit the form.
249
+ #
250
+ # You can control the form and button behavior with +html_options+. Most
251
+ # values in +html_options+ are passed through to the button element. For
252
+ # example, passing a +:class+ option within +html_options+ will set the
253
+ # class attribute of the button element.
254
+ #
255
+ # The class attribute of the form element can be set by passing a
256
+ # +:form_class+ option within +html_options+. It defaults to
257
+ # <tt>"button_to"</tt> to allow styling of the form and its children.
258
+ #
259
+ # The form submits a POST request by default. You can specify a different
260
+ # HTTP verb via the +:method+ option within +html_options+.
261
+ #
262
+ # If the HTML button generated from +button_to+ does not work with your layout, you can
263
+ # consider using the +link_to+ method with the +data-turbo-method+
264
+ # attribute as described in the +link_to+ documentation.
224
265
  #
225
266
  # ==== Options
226
- # The +options+ hash accepts the same options as +url_for+.
267
+ # The +options+ hash accepts the same options as +url_for+. To generate a
268
+ # <tt><form></tt> element without an <tt>[action]</tt> attribute, pass
269
+ # <tt>false</tt>:
270
+ #
271
+ # <%= button_to "New", false %>
272
+ # # => "<form method="post" class="button_to">
273
+ # # <button type="submit">New</button>
274
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
275
+ # # </form>"
276
+ #
277
+ # Most values in +html_options+ are passed through to the button element,
278
+ # but there are a few special options:
227
279
  #
228
- # There are a few special +html_options+:
229
280
  # * <tt>:method</tt> - \Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
230
281
  # <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
231
282
  # * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
232
283
  # * <tt>:data</tt> - This option can be used to add custom data attributes.
233
- # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
234
- # submit behavior. By default this behavior is an ajax submit.
235
284
  # * <tt>:form</tt> - This hash will be form attributes
236
285
  # * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
237
286
  # be placed
238
287
  # * <tt>:params</tt> - \Hash of parameters to be rendered as hidden fields within the form.
239
288
  #
240
- # ==== Data attributes
241
- #
242
- # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
243
- # prompt with the question specified. If the user accepts, the link is
244
- # processed normally, otherwise no action is taken.
245
- # * <tt>:disable_with</tt> - Value of this parameter will be
246
- # used as the value for a disabled version of the submit
247
- # button when the form is submitted. This feature is provided
248
- # by the unobtrusive JavaScript driver.
249
- #
250
289
  # ==== Examples
251
290
  # <%= button_to "New", action: "new" %>
252
291
  # # => "<form method="post" action="/controller/new" class="button_to">
253
- # # <input value="New" type="submit" />
292
+ # # <button type="submit">New</button>
293
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
254
294
  # # </form>"
255
295
  #
256
296
  # <%= button_to "New", new_article_path %>
257
297
  # # => "<form method="post" action="/articles/new" class="button_to">
258
- # # <input value="New" type="submit" />
298
+ # # <button type="submit">New</button>
299
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
300
+ # # </form>"
301
+ #
302
+ # <%= button_to "New", new_article_path, params: { time: Time.now } %>
303
+ # # => "<form method="post" action="/articles/new" class="button_to">
304
+ # # <button type="submit">New</button>
305
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
306
+ # # <input type="hidden" name="time" value="2021-04-08 14:06:09 -0500" autocomplete="off">
259
307
  # # </form>"
260
308
  #
261
309
  # <%= button_to [:make_happy, @user] do %>
@@ -265,49 +313,64 @@ module ActionView
265
313
  # # <button type="submit">
266
314
  # # Make happy <strong><%= @user.name %></strong>
267
315
  # # </button>
316
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
268
317
  # # </form>"
269
318
  #
270
319
  # <%= button_to "New", { action: "new" }, form_class: "new-thing" %>
271
320
  # # => "<form method="post" action="/controller/new" class="new-thing">
272
- # # <input value="New" type="submit" />
321
+ # # <button type="submit">New</button>
322
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
273
323
  # # </form>"
274
324
  #
275
- #
276
- # <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
277
- # # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
278
- # # <input value="Create" type="submit" />
279
- # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
325
+ # <%= button_to "Create", { action: "create" }, form: { "data-type" => "json" } %>
326
+ # # => "<form method="post" action="/images/create" class="button_to" data-type="json">
327
+ # # <button type="submit">Create</button>
328
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
280
329
  # # </form>"
281
330
  #
331
+ # ==== Deprecated: \Rails UJS Attributes
282
332
  #
283
- # <%= button_to "Delete Image", { action: "delete", id: @image.id },
284
- # method: :delete, data: { confirm: "Are you sure?" } %>
285
- # # => "<form method="post" action="/images/delete/1" class="button_to">
286
- # # <input type="hidden" name="_method" value="delete" />
287
- # # <input data-confirm='Are you sure?' value="Delete Image" type="submit" />
288
- # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
289
- # # </form>"
333
+ # Prior to \Rails 7, \Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following \Rails 7,
334
+ # this library is no longer on by default. This library integrated with the following options:
335
+ #
336
+ # * <tt>:remote</tt> - If set to true, will allow <tt>@rails/ujs</tt> to control the
337
+ # submit behavior. By default this behavior is an Ajax submit.
338
+ #
339
+ # <tt>@rails/ujs</tt> also integrated with the following +:data+ options:
340
+ #
341
+ # * <tt>confirm: "question?"</tt> - This will allow <tt>@rails/ujs</tt>
342
+ # to prompt with the question specified (in this case, the
343
+ # resulting text would be <tt>question?</tt>). If the user accepts, the
344
+ # button is processed normally, otherwise no action is taken.
345
+ # * <tt>:disable_with</tt> - Value of this parameter will be
346
+ # used as the value for a disabled version of the submit
347
+ # button when the form is submitted.
348
+ #
349
+ # ===== \Rails UJS Examples
290
350
  #
351
+ # <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
352
+ # # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
353
+ # # <button type="submit">Create</button>
354
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
355
+ # # </form>"
291
356
  #
292
- # <%= button_to('Destroy', 'http://www.example.com',
293
- # method: :delete, remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
294
- # # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
295
- # # <input name='_method' value='delete' type='hidden' />
296
- # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
297
- # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
298
- # # </form>"
299
- # #
300
357
  def button_to(name = nil, options = nil, html_options = nil, &block)
301
358
  html_options, options = options, name if block_given?
302
- options ||= {}
303
359
  html_options ||= {}
304
360
  html_options = html_options.stringify_keys
305
361
 
306
- url = options.is_a?(String) ? options : url_for(options)
362
+ url =
363
+ case options
364
+ when FalseClass then nil
365
+ else url_for(options)
366
+ end
367
+
307
368
  remote = html_options.delete("remote")
308
369
  params = html_options.delete("params")
309
370
 
310
- method = html_options.delete("method").to_s
371
+ authenticity_token = html_options.delete("authenticity_token")
372
+
373
+ method = (html_options.delete("method").presence || method_for_options(options)).to_s
311
374
  method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".html_safe
312
375
 
313
376
  form_method = method == "get" ? "get" : "post"
@@ -319,7 +382,7 @@ module ActionView
319
382
 
320
383
  request_token_tag = if form_method == "post"
321
384
  request_method = method.empty? ? "post" : method
322
- token_tag(nil, form_options: { action: url, method: request_method })
385
+ token_tag(authenticity_token, form_options: { action: url, method: request_method })
323
386
  else
324
387
  ""
325
388
  end
@@ -329,6 +392,8 @@ module ActionView
329
392
 
330
393
  button = if block_given?
331
394
  content_tag("button", html_options, &block)
395
+ elsif button_to_generates_button_tag
396
+ content_tag("button", name || url, html_options, &block)
332
397
  else
333
398
  html_options["value"] = name || url
334
399
  tag("input", html_options)
@@ -341,7 +406,8 @@ module ActionView
341
406
  autocomplete: "off")
342
407
  end
343
408
  end
344
- content_tag("form", inner_tags, form_options)
409
+ html = content_tag("form", inner_tags, form_options)
410
+ prevent_content_exfiltration(html)
345
411
  end
346
412
 
347
413
  # Creates a link tag of the given +name+ using a URL created by the set of
@@ -453,10 +519,10 @@ module ActionView
453
519
  # * <tt>:body</tt> - Preset the body of the email.
454
520
  # * <tt>:cc</tt> - Carbon Copy additional recipients on the email.
455
521
  # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
456
- # * <tt>:reply_to</tt> - Preset the Reply-To field of the email.
522
+ # * <tt>:reply_to</tt> - Preset the +Reply-To+ field of the email.
457
523
  #
458
524
  # ==== Obfuscation
459
- # Prior to Rails 4.0, +mail_to+ provided options for encoding the address
525
+ # Prior to \Rails 4.0, +mail_to+ provided options for encoding the address
460
526
  # in order to hinder email harvesters. To take advantage of these options,
461
527
  # install the +actionview-encoded_mail_to+ gem.
462
528
  #
@@ -467,9 +533,9 @@ module ActionView
467
533
  # mail_to "me@domain.com", "My email"
468
534
  # # => <a href="mailto:me@domain.com">My email</a>
469
535
  #
470
- # mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
536
+ # mail_to "me@domain.com", cc: "ccaddress@domain.com",
471
537
  # subject: "This is an example email"
472
- # # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
538
+ # # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">me@domain.com</a>
473
539
  #
474
540
  # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
475
541
  #
@@ -480,7 +546,7 @@ module ActionView
480
546
  # <strong>Email me:</strong> <span>me@domain.com</span>
481
547
  # </a>
482
548
  def mail_to(email_address, name = nil, html_options = {}, &block)
483
- html_options, name = name, nil if block_given?
549
+ html_options, name = name, nil if name.is_a?(Hash)
484
550
  html_options = (html_options || {}).stringify_keys
485
551
 
486
552
  extras = %w{ cc bcc body subject reply_to }.map! { |item|
@@ -556,7 +622,7 @@ module ActionView
556
622
  # We ignore any extra parameters in the request_uri if the
557
623
  # submitted URL doesn't have any either. This lets the function
558
624
  # work with things like ?order=asc
559
- # the behaviour can be disabled with check_parameters: true
625
+ # the behavior can be disabled with check_parameters: true
560
626
  request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
561
627
  request_uri = URI::DEFAULT_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)
562
628
 
@@ -583,29 +649,37 @@ module ActionView
583
649
  end
584
650
  end
585
651
 
586
- # Creates an SMS anchor link tag to the specified +phone_number+, which is
587
- # also used as the name of the link unless +name+ is specified. Additional
588
- # HTML attributes for the link can be passed in +html_options+.
652
+ # Creates an SMS anchor link tag to the specified +phone_number+. When the
653
+ # link is clicked, the default SMS messaging app is opened ready to send a
654
+ # message to the linked phone number. If the +body+ option is specified,
655
+ # the contents of the message will be preset to +body+.
589
656
  #
590
- # When clicked, an SMS message is prepopulated with the passed phone number
591
- # and optional +body+ value.
657
+ # If +name+ is not specified, +phone_number+ will be used as the name of
658
+ # the link.
592
659
  #
593
- # +sms_to+ has a +body+ option for customizing the SMS message itself by
594
- # passing special keys to +html_options+.
660
+ # A +country_code+ option is supported, which prepends a plus sign and the
661
+ # given country code to the linked phone number. For example,
662
+ # <tt>country_code: "01"</tt> will prepend <tt>+01</tt> to the linked
663
+ # phone number.
664
+ #
665
+ # Additional HTML attributes for the link can be passed via +html_options+.
595
666
  #
596
667
  # ==== Options
668
+ # * <tt>:country_code</tt> - Prepend the country code to the phone number.
597
669
  # * <tt>:body</tt> - Preset the body of the message.
598
670
  #
599
671
  # ==== Examples
600
672
  # sms_to "5155555785"
601
673
  # # => <a href="sms:5155555785;">5155555785</a>
602
674
  #
675
+ # sms_to "5155555785", country_code: "01"
676
+ # # => <a href="sms:+015155555785;">5155555785</a>
677
+ #
603
678
  # sms_to "5155555785", "Text me"
604
679
  # # => <a href="sms:5155555785;">Text me</a>
605
680
  #
606
- # sms_to "5155555785", "Text me",
607
- # body: "Hello Jim I have a question about your product."
608
- # # => <a href="sms:5155555785;?body=Hello%20Jim%20I%20have%20a%20question%20about%20your%20product">Text me</a>
681
+ # sms_to "5155555785", body: "I have a question about your product."
682
+ # # => <a href="sms:5155555785;?body=I%20have%20a%20question%20about%20your%20product">5155555785</a>
609
683
  #
610
684
  # You can use a block as well if your link target is hard to fit into the name parameter. \ERB example:
611
685
  #
@@ -616,46 +690,47 @@ module ActionView
616
690
  # <strong>Text me:</strong>
617
691
  # </a>
618
692
  def sms_to(phone_number, name = nil, html_options = {}, &block)
619
- html_options, name = name, nil if block_given?
693
+ html_options, name = name, nil if name.is_a?(Hash)
620
694
  html_options = (html_options || {}).stringify_keys
621
695
 
622
- extras = %w{ body }.map! { |item|
623
- option = html_options.delete(item).presence || next
624
- "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
625
- }.compact
626
- extras = extras.empty? ? "" : "?&" + extras.join("&")
696
+ country_code = html_options.delete("country_code").presence
697
+ country_code = country_code ? "+#{ERB::Util.url_encode(country_code)}" : ""
698
+
699
+ body = html_options.delete("body").presence
700
+ body = body ? "?&body=#{ERB::Util.url_encode(body)}" : ""
627
701
 
628
702
  encoded_phone_number = ERB::Util.url_encode(phone_number)
629
- html_options["href"] = "sms:#{encoded_phone_number};#{extras}"
703
+ html_options["href"] = "sms:#{country_code}#{encoded_phone_number};#{body}"
630
704
 
631
705
  content_tag("a", name || phone_number, html_options, &block)
632
706
  end
633
707
 
634
- # Creates a TEL anchor link tag to the specified +phone_number+, which is
635
- # also used as the name of the link unless +name+ is specified. Additional
636
- # HTML attributes for the link can be passed in +html_options+.
708
+ # Creates a TEL anchor link tag to the specified +phone_number+. When the
709
+ # link is clicked, the default app to make phone calls is opened and
710
+ # prepopulated with the phone number.
711
+ #
712
+ # If +name+ is not specified, +phone_number+ will be used as the name of
713
+ # the link.
637
714
  #
638
- # When clicked, the default app to make calls is opened, and it
639
- # is prepopulated with the passed phone number and optional
640
- # +country_code+ value.
715
+ # A +country_code+ option is supported, which prepends a plus sign and the
716
+ # given country code to the linked phone number. For example,
717
+ # <tt>country_code: "01"</tt> will prepend <tt>+01</tt> to the linked
718
+ # phone number.
641
719
  #
642
- # +phone_to+ has an optional +country_code+ option which automatically adds the country
643
- # code as well as the + sign in the phone numer that gets prepopulated,
644
- # for example if +country_code: "01"+ +\+01+ will be prepended to the
645
- # phone numer, by passing special keys to +html_options+.
720
+ # Additional HTML attributes for the link can be passed via +html_options+.
646
721
  #
647
722
  # ==== Options
648
- # * <tt>:country_code</tt> - Prepends the country code to the number
723
+ # * <tt>:country_code</tt> - Prepends the country code to the phone number
649
724
  #
650
725
  # ==== Examples
651
726
  # phone_to "1234567890"
652
727
  # # => <a href="tel:1234567890">1234567890</a>
653
728
  #
654
729
  # phone_to "1234567890", "Phone me"
655
- # # => <a href="tel:134567890">Phone me</a>
730
+ # # => <a href="tel:1234567890">Phone me</a>
656
731
  #
657
- # phone_to "1234567890", "Phone me", country_code: "01"
658
- # # => <a href="tel:+015155555785">Phone me</a>
732
+ # phone_to "1234567890", country_code: "01"
733
+ # # => <a href="tel:+011234567890">1234567890</a>
659
734
  #
660
735
  # You can use a block as well if your link target is hard to fit into the name parameter. \ERB example:
661
736
  #
@@ -666,7 +741,7 @@ module ActionView
666
741
  # <strong>Phone me:</strong>
667
742
  # </a>
668
743
  def phone_to(phone_number, name = nil, html_options = {}, &block)
669
- html_options, name = name, nil if block_given?
744
+ html_options, name = name, nil if name.is_a?(Hash)
670
745
  html_options = (html_options || {}).stringify_keys
671
746
 
672
747
  country_code = html_options.delete("country_code").presence
@@ -694,6 +769,14 @@ module ActionView
694
769
  end
695
770
  end
696
771
 
772
+ def url_target(name, options)
773
+ if name.respond_to?(:model_name) && options.is_a?(Hash) && options.empty?
774
+ url_for(name)
775
+ else
776
+ url_for(options)
777
+ end
778
+ end
779
+
697
780
  def link_to_remote_options?(options)
698
781
  if options.is_a?(Hash)
699
782
  options.delete("remote") || options.delete(:remote)
@@ -711,6 +794,16 @@ module ActionView
711
794
  html_options["data-method"] = method
712
795
  end
713
796
 
797
+ def method_for_options(options)
798
+ if options.is_a?(Array)
799
+ method_for_options(options.last)
800
+ elsif options.respond_to?(:persisted?)
801
+ options.persisted? ? :patch : :post
802
+ elsif options.respond_to?(:to_model)
803
+ method_for_options(options.to_model)
804
+ end
805
+ end
806
+
714
807
  STRINGIFIED_COMMON_METHODS = {
715
808
  get: "get",
716
809
  delete: "delete",
@@ -726,7 +819,12 @@ module ActionView
726
819
 
727
820
  def token_tag(token = nil, form_options: {})
728
821
  if token != false && defined?(protect_against_forgery?) && protect_against_forgery?
729
- token ||= form_authenticity_token(form_options: form_options)
822
+ token =
823
+ if token == true || token.nil?
824
+ form_authenticity_token(form_options: form_options.merge(authenticity_token: token))
825
+ else
826
+ token
827
+ end
730
828
  tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token, autocomplete: "off")
731
829
  else
732
830
  ""