Commit Graph

405 Commits

Author SHA1 Message Date
Rafael Mendonça França 23344d4b8c
Remove deprecated constant `ActionView::Path` 2023-03-03 00:38:37 +00:00
Sean Doyle 812e50b00a `token_list`: Guard Stimulus' `data-action` from multiple escapes
Prior to this commit, chaining more than one `token_list` calls with a
[data-action][] attribute value would result in one too many HTML
escapes. Additional subsequent calls would compound the problem.

For example, the following calls would result in an invalid descriptor
that's escaped too many times to be parsed.

```ruby
first   = "click->controller#action1"
second  = "click->controller#action2"
third   = "click->controller#action3"
fourth  = "click->controller#action4"

value = token_list(first, token_list(second, token_list(third)))

CGI.unescape_html value.to_s
 # => "click->controller#action1 click->controller#action2 click->controller#action3 click->controller#action4"
```

By [CGI.unescape_html][] each `String` value before passing it to
[token_list][] (which re-escapes the value), we can preserve a lossless
concatenation process while also preserving the HTML safety.

After this commit, the previous example works as expected:

```ruby
first   = "click->controller#action1"
second  = "click->controller#action2"
third   = "click->controller#action3"
fourth  = "click->controller#action4"

value = token_list(first, token_list(second, token_list(third)))

CGI.unescape_html value.to_s
 # => "click->controller#action1 click->controller#action2 click->controller#action3 click->controller#action4"
```

[unescaping]: https://ruby-doc.org/stdlib-2.5.3/libdoc/cgi/rdoc/CGI/Util.html#method-i-unescape_html
[token_list]:
https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-token_list
[data-action]: https://stimulus.hotwired.dev/reference/actions
2023-02-08 11:53:02 -05:00
Alex Ghiculescu 49c2e51808 Allow `f.select` to be called with a single hash containing options and HTML options
I do this a lot:

```erb
<%= select :post, :author, authors, required: true %>
```

It doesn't work; the `required` attribute is ignored! Instead, you need to do this:

```erb
<%= select :post, :author, authors, {}, required: true %>
```

It's hard to remember the right API, and it looks to me like a code smell. It looks even smellier when you end up with this:

```erb
<%= select :post, :author, authors, { include_blank: "Choose an option" }, { required: true } %>
```

Where this would be nicer, but again, the `required` attribute is ignored:

```erb
<%= select :post, :author, authors, include_blank: "Choose an option", required: true %>
```

This PR implements a special handling for `required`, `multiple`, and `size` HTML attributes so that these now do the same thing:

```erb
<%= select :post, :author, authors, include_blank: "Choose an option", required: true %>
<%= select :post, :author, authors, { include_blank: "Choose an option" }, { required: true } %>
```

ps. as proof I'm not the only person who makes this mistake, one of the tests in the Rails test suite was wrong! The test added in https://github.com/rails/rails/pull/40522 puts the `multiple` attribute in the wrong place and has the wrong assertion as as result. This PR includes a fix for the test.
2022-12-13 11:44:34 -06:00
Andrey Samsonov c20b629f52 Fix inconsistent behavior in form helper date/time tags with options; issue #46677
This change makes date/time options (value, min, max) in `time_field`, `date_field`, `datetime_field`, `week_field`, `month_field` form helpers behave in a unified way.
2022-12-11 17:19:18 +01:00
Rafael Mendonça França db5a6ef624
Merge PR #43019 2022-11-23 19:14:33 +00:00
Alex Ghiculescu 4fcac154f4 Support `checked` as a keyword argument in `check_box_tag` and `radio_button_tag`
Currently if you do this:

```ruby
check_box_tag "admin", "1", checked: false
```

It is treated [as truthy](19f9922523/actionview/lib/action_view/helpers/form_tag_helper.rb (L444)), and your checkbox is checked. This can be a bit surprising, particularly because the `FormHelper` version [does support](19f9922523/actionview/lib/action_view/helpers/form_helper.rb (L1285)) a keyword argument.

```ruby
f.check_box "admin", checked: false
```

So this PR updates `check_box_tag` and `radio_button_tag` to support `checked` as a positional or keyword argument, this way you can use the same API in both cases.

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2022-10-21 12:41:29 -05:00
Goulven Champenois f4f15c86a2 Allow passing a class to `dom_id`
You no longer need to call `new` when passing a class to `dom_id`.
This makes `dom_id` behave like `dom_class` in this regard.
Apart from saving a few keystrokes, it prevents Ruby from needing to
instantiate a whole new object just to generate a string.

Before:
```ruby
dom_id(Post) # NoMethodError: undefined method `to_key' for Post:Class
```

After:
```ruby
dom_id(Post) # "new_post"
```

You can still call `dom_id(Post.new)`.
2022-09-20 16:53:12 +02:00
Aaron Gough b451ff098d Add :locals to ActionView rendering instrumentation 2022-09-08 19:44:09 -04:00
Max Chernyak fe554a3daf Chomp the break_sequence in word_wrap
When word_wrap's break_sequence contains non-rstrippable
characters, word_wrap fails to strip the extra break_sequence at the
end properly. Using chomp! helps cut exactly what was specified in the
argument.
2022-09-05 15:38:23 -04:00
Joel Hawksley b7908a62f9 Rename 'Explicit Locals` to `Strict Locals`
Per https://github.com/rails/rails/pull/45602#discussion_r934981516
2022-08-01 17:23:47 -06:00
Joel Hawksley bbe7d19e11 Allow templates to define which locals they accept. 2022-08-01 15:42:02 -06:00
Wojciech Wnętrzak 2dea9aebf2
Add include_seconds option to datetime_local_field.
This allows to omit seconds part in the input field, by passing `include_seconds: false`

It's a follow up to https://github.com/rails/rails/pull/41728
2022-07-06 22:27:04 +02:00
Sean Doyle 8a0bc4aa90 Support calls to `#field_name` with nil `object_name`
It's possible for `ActionView::Helpers::FormTagHelper#field_name` calls
made by instances constructed through `fields` and `fields_for` helpers
to have an `object_name` argument that's `nil`. For example, the
following will raise an `undefined method `empty?' for nil:NilClass`
exception:

```erb
<%= fields do |f| %>
  <%= f.field_name :body %>
<% end %>
```

To guard against those calls, replace the method's call to
`String#empty?` with `Object#blank?`, since `NilClass#empty?` is not
defined.
2022-06-15 18:51:20 +01:00
Mike Dalessio 5f8f6764d8
Strings returned from `strip_tags` are correctly tagged `html_safe?`
Because these strings contain no HTML elements and the basic entities
are escaped, they are safe to be included as-is as PCDATA in HTML
content. Tagging them as html-safe avoids double-escaping entities
when being concatenated to a SafeBuffer during rendering.

Fixes https://github.com/rails/rails-html-sanitizer/issues/124
2022-05-31 10:02:07 -04:00
Sean Doyle 980de46f54 Move `convert_to_model` call from `form_for` to `form_with`
Ensure models passed to `form_with` attempt to call `to_model`.

Now that `form_for` is implemented in terms of `form_with`, this commit
also removes the `convert_to_model` call from the `form_for` implementation.

To exercise this behavior, change existing `form_with` test coverage.

Prior to this change, a call to `form_with` made with a `model:` argument
that responds to `to_model` would not incorporate the instance's persistence
state into the form's HTTP verb. After this change, the persistence state
inferred from the `model:` argument's `to_model` call is incorporated into
the `<form>` element's `[method]` attribute.

This is a separate follow-up change proposed in [rails/rails#44328][].
The original change to restore old behavior _deliberately_ excluded
applying the same logic to `form_with`, since it would be a breaking
change from how `form_with` behaved previously.

This commit proposed making that breaking change.

[rails/rails#44328]: https://github.com/rails/rails/pull/44328#discussion_r808475585
2022-05-25 09:48:58 -04:00
Alvaro Martin Fraguas 649516ce0f
Fix and add protections for XSS in names.
Add the method ERB::Util.xml_name_escape to escape dangerous characters
in names of tags and names of attributes, following the specification of
XML.

Use that method in the tag helpers of ActionView::Helpers. Rename the option
:escape_attributes to :escape, to simplify by applying the option to the whole
tag.
2022-04-26 12:34:42 -07:00
Matheus Richard 414394206a Extend audio_tag and video_tag to accept Active Storage attachments.
Now it's possible to write

    audio_tag(user.audio_file)
    video_tag(user.video_file)

Instead of

    audio_tag(polymorphic_path(user.audio_file))
    video_tag(polymorphic_path(user.video_file))

image_tag already supported that, so this follows the same pattern.
2022-03-14 02:05:37 -03:00
Sean Doyle 9dc083f95d `form_for`: Attempt to call `to_model` on record
Related to https://github.com/rails/rails/issues/44326

---

The changes introduced in [rails/rails#43421][] did not account for
attempting to coerce the record with [#to_model][]. This commit resolves
that issue and adds test coverage to guard against future regressions.

Additionally, this adds tests to exercise `form_with` to ensure that it
_does not_ call `#to_model` on its `model:` argument, since that was the
behavior in [6.1.4.4][]

[rails/rails#43421]: https://github.com/rails/rails/pull/43421
[#to_model]: https://edgeapi.rubyonrails.org/classes/ActiveModel/Conversion.html#method-i-to_model
[6.1.4.4]: https://github.com/rails/rails/issues/44326#issuecomment-1029344241
2022-02-17 16:01:57 -05:00
Chris Gunther 0387cc15a7 Add support for HTML attributes of optgroups to select helper
PR #11517 updated `grouped_options_for_select` to allow passing HTML
attributes of the optgroup as the last element of each array, which is
called internally by `select` when it detects grouped choices.

However, the `select` helper detected grouped choices by seeing if the
[last element is an Array](6ec669b65d/actionview/lib/action_view/helpers/tags/select.rb (L37)),
meaning if you passed a hash of HTML attributes, it would no longer
treat the choices as grouped. This conflicted with
`grouped_options_for_select`, which assumes the individual options are
the [second element](6ec669b65d/actionview/lib/action_view/helpers/form_options_helper.rb (L546)),
not the last element.

Now there's agreement between `select` and `grouped_options_for_select`
in expecting the individual option choices to be the second element,
allowing the hash of HTML attributes to exist as the last element and
properly trigger grouped options.
2022-02-14 09:50:40 -05:00
Rafael Mendonça França dae7e46db4
Remove CHANGELOG entry that is already in 7-0-stable 2021-12-15 00:55:18 +00:00
Sean Doyle 4f191b9754 ActiveStorage: support empty attachments submits
The background
---

Configuration for replacing a collection was introduced in
[rails/rails#36716][].

However, since [rails/rails#42596][] has been merged, Rails 7.1 and
beyond will default to _replacing_ an Active Storage `has_many_attached`
relationship, as opposed to _appending to it_.

The problem
---

With replacement as the established precedent, it's currently a
challenge to replace an existing collection with an empty one.

The solution
---

This commit makes two changes.

The first is to Action View and its form building helpers. The change
draws inspiration from how an `<input type="checkbox">` field (or
collection of fields) is paired with an `<input type="hidden">` field to
represent the unchecked value. The change pairs any `<input type="file"
multiple="multiple">` elements with an `<input type="hidden">` element
to represent an empty collection. Like the [check_box][] form builder
method, the `file_field`  method accepts an `include_hidden:` option to
skip the creation of the hidden element.

The second is to how Active Storage generates attribute assignment
methods through `has_many_attached`. With the possibility of an `<input
type="file">` field being paired with an `<input type="hidden"
value="">` field, the backing models need to be able to coerce an
"empty-ish" value into an empty list. For example:

```ruby
@user.highlights = [""]
@user.highlights        # => []
```

When combined, these changes enable consumer applications to submit
"empty" collections to blank out existing attachments.

Support is configured through the
`config.active_storage.multiple_file_field_include_hidden` configuration
value, which defaults to `false`.

[check_box]: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-check_box
[rails/rails#36716]: https://github.com/rails/rails/pull/36716
[rails/rails#42596]: https://github.com/rails/rails/pull/42596
2021-12-14 18:40:35 -05:00
Rafael Mendonça França 83d85b2207
Start Rails 7.1 development 2021-12-07 15:52:30 +00:00
Rafael Mendonça França 846f9e9595
Merge PR #43416 2021-11-15 22:55:37 +00:00
Rafael Mendonça França 033822117b
Merge PR #43413 2021-11-15 22:35:01 +00:00
Sean Doyle 15f6113622 Support name Symbol to `FormBuilder#button`
Since `<button>` elements translate their `[name]` and `[value]`
attributes to the resulting `<form>` element submission, and are encoded
into the resulting `URLSearchParams` or `FormData` instance, Action View
`FormBuilder` instances should support encoding a method name the same
way it does for other fields.

For instance, consider this HTML:

```html
<button>Publish</button>
<button name="post[draft]" value="true">Save as draft</button>
```

Clicking the "Publish" button would submit the form without encoding any
additional `[name]` and `[value]` pairs.

Clicking the "Save as draft" button would submit the form and encode
`post[draft]=true` into the submission.

This commit changes the `FormBuilder#button` method to interpret a
`Symbol` as the first argument as a method name argument, and encodes
its value based on the form's `model:` or `scope:` value:

```erb
<%= form.button :draft, value: true do %>
  Save as draft
<% end %>
end
<%# =>  <button name="post[draft]" value="true" type="submit">  %>
<%#       Save as draft                                         %>
<%#     </button>                                               %>
```

Co-authored-by: Rafael Mendonça França <rafael@rubyonrails.org>
2021-11-15 21:40:06 +00:00
Rafael Mendonça França 876d2ff904
Merge PR #43409 2021-11-15 21:20:34 +00:00
Rafael Mendonça França 27d24e9caa
Merge PR #42755 2021-11-15 21:11:59 +00:00
Guillermo Iguaran b667e48b22
Merge branch 'main' into button-to-authenticity-token 2021-11-14 11:36:48 -08:00
Sean Doyle 3dae4469af Support `<form>` elements without `[action]`
Some background
---

By default, when a `<form>` is declared without an `[action]` attribute,
browsers will encode a `<form>`'s fields into the _current_ URL.

This can be useful for a `<form method="get">` that operates on the
current page. For example, when filtering search results, a form that
sorts:

```html
<form method="get">
  <button name="sort" value="desc">Most to least</button>
  <button name="sort" value="asc">Least to most</button>
</form>
```

can operate on a page that is filtered in another way by merging the
`?sort=asc` or `?sort=desc` values _into_ the existing page, which might
have the `?q=...` string set elsewhere.

The problem
---

Prior to this commit, none of the `<form>` construction variations
supported declaring a `<form>` without an `[action]` attribute.
`form_with`, `form_for`, and `form_tag` all default to `url_for({})`
when a `url:` or `action:` option is omitted.

The solution
---

Treat `url: false`, or `action: false` as an escape hatch to signal to
Action View that we don't need to transform the `model:` option or
argument into a Rails route.

Similarly, when calling `button_to` with `false` as the URL options
arguments will construct a `<form>` element without an `[action]`
attribute.
2021-10-29 10:08:47 -04:00
Sean Doyle 9c86593caa Execute `field_error_proc` within view
Instead of treating it as an anonymous block, execute the
`ActionView::Base.field_error_proc` within the context of the
`ActionView::Base` instance.

This enables consumer applications to continue to override the proc as
they see fit, but frees them from declaring templating logic within a
`config/initializers/*.rb`, `config/environments/*.rb` or
`config/application.rb` file.

This makes it possible to replace something like:

```ruby
config.action_view.field_error_proc = proc do |html_tag, instance|
  <<~HTML.html_safe
    #{html_tag}
    <span class="errors">#{instance.error_message.to_sentence}</span>
  HTML
end
```

With inline calls to Action View helpers like:

```ruby
config.action_view.field_error_proc = proc do |html_tag, instance|
  safe_join [ html_tag, tag.span(instance.error_message.to_sentence, class: "errors") ]
end
```

Or with a view partial rendering, like:

```ruby
config.action_view.field_error_proc = proc do |html_tag, instance|
  render partial: "application/field_with_errors", locals: { html_tag: html_tag, instance: instance }
end
```

Then, elsewhere in `app/views/application/field_with_errors.html.erb`:

```erb
<%= html_tag %>
<span class="errors"><%= instance.error_message.to_sentence %></span>
```
2021-10-29 10:07:04 -04:00
Sean Doyle 71fb8eb833 button_to: Support `authenticity_token:` option
Match support for passing `authenticity_token:` in `form_with` and
`form_for` calls.

```ruby
button_to "Create", Post.new, authenticity_token: false
  # => <form class="button_to" method="post" action="/posts"><button type="submit">Create</button></form>

button_to "Create", Post.new, authenticity_token: true
  # => <form class="button_to" method="post" action="/posts"><button type="submit">Create</button><input type="hidden" name="form_token" value="abc123..." autocomplete="off" /></form>

button_to "Create", Post.new, authenticity_token: "secret"
  # => <form class="button_to" method="post" action="/posts"><button type="submit">Create</button><input type="hidden" name="form_token" value="secret" autocomplete="off" /></form>
```
2021-10-29 10:02:22 -04:00
Sean Doyle 46175a036a Action View: Support `fields model: [...]`
Support `fields model: [@nested, @model]` the same way as `form_with
model: [@nested, @model]`.

After this change, the `fields` helper matches the precedent established
by [fields_for][], [form_for][], and [form_with][].

[fields_for]: 5e1a039a1d/actionview/lib/action_view/helpers/form_helper.rb (L2235)
[form_with]: 5e1a039a1d/actionview/lib/action_view/helpers/form_helper.rb (L749)
[form_for]: 5e1a039a1d/actionview/lib/action_view/helpers/form_helper.rb (L436)
2021-10-29 10:01:51 -04:00
Sean Doyle 7d2be2e011 Make `button_to` more model-aware
Infer HTTP verb `[method]` from a model or Array with model as the first
argument to `button_to` when combined with a block:

```ruby
button_to(Workshop.find(1)){ "Update" }
  #=> <form method="post" action="/workshops/1" class="button_to">
  #=>   <input type="hidden" name="_method" value="patch" autocomplete="off" />
  #=>   <button type="submit">Update</button>
  #=> </form>

button_to([ Workshop.find(1), Session.find(1) ]) { "Update" }
  #=> <form method="post" action="/workshops/1/sessions/1" class="button_to">
  #=>   <input type="hidden" name="_method" value="patch" autocomplete="off" />
  #=>   <button type="submit">Update</button>
  #=> </form>
```

Prior to this change, the constructed `<form>` was always submitted with
a `[method="post"]` and _always_ omitted the `<input type="hidden"
name="_method" value="...">` field, regardless of the return value of
the "model" argument's `#persisted?` predicate.
2021-10-29 10:01:20 -04:00
Sean Doyle 37081bf507 Introduce `field_name` view helper
The `field_name` helper and corresponding `FormBuilder#field_name`
method provide an Action View-compliant way of overriding a form field
element's `[name]` attribute (similar to `field_id` and
`FormBuilder#field_id` introduced in rails/rails#40127[][]).

```ruby
text_field_tag :post, :title, name: field_name(:post, :title, :subtitle)
  # => <input type="text" name="post[title][subtitle]">

text_field_tag :post, :tag, name: field_name(:post, :tag, multiple: true)
  # => <input type="text" name="post[tag][]">

form_for @post do |f|
  f.field_tag :tag, name: f.field_name(:tag, multiple: true)
  # => <input type="text" name="post[tag][]">
end
```

[rails/rails#40127]: https://github.com/rails/rails/pull/40127
2021-10-29 10:00:15 -04:00
Shunichi Ikegami e9fa24fca5 Add :day_format option to date_select
date_select("article", "written_on", day_format: ->(day) { day.ordinalize })

generates day options like

  <option value="1">1st</option>\n<option value="2">2nd</option>...
2021-10-28 13:51:20 +09:00
Rafael Mendonça França 8e86e87219
Merge PR #42234 2021-09-21 18:54:48 -04:00
Rafael Mendonça França 040e22b104
Fix CHANGELOG [ci skip] 2021-09-20 17:35:12 -04:00
Alexey Vasiliev 6b7ff4f933
Support svg unpaired tags in tag helper 2021-09-16 21:50:54 +03:00
Rafael Mendonça França d177551c30
Preparing for 7.0.0.alpha2 release 2021-09-15 18:22:51 -04:00
Rafael Mendonça França 9b7be48212
Preparing for 7.0.0.alpha1 release 2021-09-15 17:55:08 -04:00
Mike Dalessio 900ce92c9d
Avoid use of exceptions to detect invalid floats
Use Kernel::Float(..., exceptions:false) instead of a rescue block in
ActionView::Helpers::NumberHelper and
ActiveSupport::NumberHelper::NumberConverter to slightly improve
performance.

Also remove documentation that incorrectly states
ActiveSupport::NumberHelper supports the `raise:` option.
2021-08-25 21:44:45 -04:00
Nate Berkopec 46bfd082b0
Make preload_link_tag work with images 2021-08-24 13:49:02 -06:00
Olivier Lacan 1e9629e8a9 Allow url_for to infer the name of the link from Model#to_s 2021-08-17 12:17:04 -04:00
Drew Bragg 592570f1bf Add `weekday_options_for_select` method
Add `weekday_select` method

Create `Tags::WeekdaySelect` class

Add `weekday_select` to `FromBuilder`

Add Documentation

Allow `WeekdaySelect` to use selected option if value is nil

Doc fix

Add tests

Use kwrd args

Update CHANGELOG

Fix `Tags::WeekdaySelect` for updated kwrd args

Update CHANGELOG format

Condense `weekday_options_for_select` method

Update tests for kwargs
2021-08-16 13:04:33 -04:00
Petrik de Heus 6828334555
Add missing dots to changelogs 2021-08-07 18:53:10 +02:00
Carlos Antonio da Silva 65bbfa9519 Fix typo in Action View changelog [ci skip] 2021-08-05 11:03:08 -03:00
Joel Hawksley fd5792f0fc
Add caching? helper method
Caching something that shouldn't be cached is a potential source of
bugs and security vulnerabilities. For example, one could write a
form helper that outputs a request-specific auth token, only for
the helper to be used inside of a `cache` block.

In the GitHub application, we implemented a caching? method and used
it to raise an error if a specific code path is being cached that
we don't want to be cached.

I've credited its original author, @btoews.

Co-authored-by: Ben Toews <mastahyeti@gmail.com>
Co-authored-by: John Hawthorn <jhawthorn@github.com>
Co-authored-by: Kasper Timm Hansen <kaspth@gmail.com>
2021-08-03 15:49:48 -06:00
Rafael Mendonça França 6741222f65
Merge pull request #41728 2021-07-29 01:11:59 +00:00
Ryuta Kamizono e50b0e3ab3 Fixup CHANGELOGs [ci skip] 2021-07-21 10:08:08 +09:00
Ted 0c528034cf
Update actionview/CHANGELOG.md
Co-authored-by: Petrik de Heus <petrik@deheus.net>
2021-07-12 09:16:25 -07:00