actionview 7.0.1 → 7.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +281 -202
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/app/assets/javascripts/rails-ujs.esm.js +693 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +33 -12
- data/lib/action_view/buffers.rb +106 -8
- data/lib/action_view/cache_expiry.rb +40 -43
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +1 -1
- data/lib/action_view/gem_version.rb +2 -2
- data/lib/action_view/helpers/active_model_helper.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +133 -48
- data/lib/action_view/helpers/asset_url_helper.rb +13 -12
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
- data/lib/action_view/helpers/cache_helper.rb +3 -9
- data/lib/action_view/helpers/capture_helper.rb +26 -12
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +6 -0
- data/lib/action_view/helpers/csp_helper.rb +2 -2
- data/lib/action_view/helpers/csrf_helper.rb +3 -3
- data/lib/action_view/helpers/date_helper.rb +76 -64
- data/lib/action_view/helpers/debug_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +62 -31
- data/lib/action_view/helpers/form_options_helper.rb +6 -3
- data/lib/action_view/helpers/form_tag_helper.rb +88 -44
- data/lib/action_view/helpers/javascript_helper.rb +1 -0
- data/lib/action_view/helpers/number_helper.rb +15 -13
- data/lib/action_view/helpers/output_safety_helper.rb +4 -4
- data/lib/action_view/helpers/rendering_helper.rb +5 -6
- data/lib/action_view/helpers/sanitize_helper.rb +34 -15
- data/lib/action_view/helpers/tag_helper.rb +27 -16
- data/lib/action_view/helpers/tags/base.rb +11 -52
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
- data/lib/action_view/helpers/tags/collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/date_field.rb +1 -1
- data/lib/action_view/helpers/tags/date_select.rb +2 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
- data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/month_field.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +4 -1
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/time_field.rb +1 -1
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
- data/lib/action_view/helpers/tags/week_field.rb +1 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
- data/lib/action_view/helpers/tags.rb +2 -0
- data/lib/action_view/helpers/text_helper.rb +33 -17
- data/lib/action_view/helpers/translation_helper.rb +6 -6
- data/lib/action_view/helpers/url_helper.rb +90 -65
- data/lib/action_view/helpers.rb +2 -0
- data/lib/action_view/layouts.rb +13 -8
- data/lib/action_view/log_subscriber.rb +49 -32
- data/lib/action_view/lookup_context.rb +29 -13
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +13 -14
- data/lib/action_view/railtie.rb +26 -3
- data/lib/action_view/record_identifier.rb +16 -9
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- data/lib/action_view/renderer/collection_renderer.rb +9 -1
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
- data/lib/action_view/renderer/partial_renderer.rb +3 -2
- data/lib/action_view/renderer/renderer.rb +2 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
- data/lib/action_view/renderer/template_renderer.rb +3 -2
- data/lib/action_view/rendering.rb +24 -6
- data/lib/action_view/ripper_ast_parser.rb +6 -6
- data/lib/action_view/routing_url_for.rb +7 -4
- data/lib/action_view/template/error.rb +14 -1
- data/lib/action_view/template/handlers/builder.rb +4 -4
- data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
- data/lib/action_view/template/handlers/erb.rb +73 -1
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/html.rb +1 -1
- data/lib/action_view/template/raw_file.rb +1 -1
- data/lib/action_view/template/renderable.rb +1 -1
- data/lib/action_view/template/resolver.rb +15 -5
- data/lib/action_view/template/text.rb +1 -1
- data/lib/action_view/template/types.rb +25 -34
- data/lib/action_view/template.rb +227 -53
- data/lib/action_view/template_path.rb +2 -0
- data/lib/action_view/test_case.rb +174 -21
- data/lib/action_view/unbound_template.rb +15 -5
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +19 -28
- data/lib/action_view.rb +4 -1
- data/lib/assets/compiled/rails-ujs.js +36 -5
- metadata +27 -27
@@ -5,17 +5,18 @@ require "active_support/core_ext/string/output_safety"
|
|
5
5
|
require "active_support/number_helper"
|
6
6
|
|
7
7
|
module ActionView
|
8
|
-
# = Action View Number Helpers
|
9
8
|
module Helpers # :nodoc:
|
9
|
+
# = Action View Number \Helpers
|
10
|
+
#
|
10
11
|
# Provides methods for converting numbers into formatted strings.
|
11
12
|
# Methods are provided for phone numbers, currency, percentage,
|
12
|
-
# precision, positional notation, file size and pretty printing.
|
13
|
+
# precision, positional notation, file size, and pretty printing.
|
13
14
|
#
|
14
15
|
# Most methods expect a +number+ argument, and will return it
|
15
16
|
# unchanged if can't be converted into a valid number.
|
16
17
|
module NumberHelper
|
17
18
|
# Raised when argument +number+ param given to the helpers is invalid and
|
18
|
-
# the option
|
19
|
+
# the option +:raise+ is set to +true+.
|
19
20
|
class InvalidNumberError < StandardError
|
20
21
|
attr_accessor :number
|
21
22
|
def initialize(number)
|
@@ -202,7 +203,7 @@ module ActionView
|
|
202
203
|
# number_with_delimiter("123456.78",
|
203
204
|
# delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) # => "1,23,456.78"
|
204
205
|
#
|
205
|
-
#
|
206
|
+
# number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
|
206
207
|
def number_with_delimiter(number, options = {})
|
207
208
|
delegate_number_helper_method(:number_to_delimited, number, options)
|
208
209
|
end
|
@@ -370,13 +371,14 @@ module ActionView
|
|
370
371
|
# out by default (set <tt>:strip_insignificant_zeros</tt> to
|
371
372
|
# +false+ to change that):
|
372
373
|
#
|
373
|
-
#
|
374
|
-
#
|
374
|
+
# number_to_human(12.00001) # => "12"
|
375
|
+
# number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0"
|
375
376
|
#
|
376
377
|
# ==== Custom Unit Quantifiers
|
377
378
|
#
|
378
379
|
# You can also use your own custom unit quantifiers:
|
379
|
-
#
|
380
|
+
#
|
381
|
+
# number_to_human(500000, units: {unit: "ml", thousand: "lt"}) # => "500 lt"
|
380
382
|
#
|
381
383
|
# If in your I18n locale you have:
|
382
384
|
# distance:
|
@@ -393,12 +395,12 @@ module ActionView
|
|
393
395
|
#
|
394
396
|
# Then you could do:
|
395
397
|
#
|
396
|
-
#
|
397
|
-
#
|
398
|
-
#
|
399
|
-
#
|
400
|
-
#
|
401
|
-
#
|
398
|
+
# number_to_human(543934, units: :distance) # => "544 kilometers"
|
399
|
+
# number_to_human(54393498, units: :distance) # => "54400 kilometers"
|
400
|
+
# number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
|
401
|
+
# number_to_human(343, units: :distance, precision: 1) # => "300 meters"
|
402
|
+
# number_to_human(1, units: :distance) # => "1 meter"
|
403
|
+
# number_to_human(0.34, units: :distance) # => "34 centimeters"
|
402
404
|
#
|
403
405
|
def number_to_human(number, options = {})
|
404
406
|
delegate_number_helper_method(:number_to_human, number, options)
|
@@ -3,18 +3,18 @@
|
|
3
3
|
require "active_support/core_ext/string/output_safety"
|
4
4
|
|
5
5
|
module ActionView # :nodoc:
|
6
|
-
# = Action View Raw Output Helper
|
7
6
|
module Helpers # :nodoc:
|
7
|
+
# = Action View Raw Output \Helpers
|
8
8
|
module OutputSafetyHelper
|
9
9
|
# This method outputs without escaping a string. Since escaping tags is
|
10
|
-
# now default, this can be used when you don't want Rails to automatically
|
10
|
+
# now default, this can be used when you don't want \Rails to automatically
|
11
11
|
# escape tags. This is not recommended if the data is coming from the user's
|
12
12
|
# input.
|
13
13
|
#
|
14
14
|
# For example:
|
15
15
|
#
|
16
|
-
#
|
17
|
-
#
|
16
|
+
# raw @user.name
|
17
|
+
# # => 'Jimmy <alert>Tables</alert>'
|
18
18
|
def raw(stringish)
|
19
19
|
stringish.to_s.html_safe
|
20
20
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActionView
|
4
4
|
module Helpers # :nodoc:
|
5
|
-
# = Action View Rendering
|
5
|
+
# = Action View \Rendering \Helpers
|
6
6
|
#
|
7
7
|
# Implements methods that allow rendering from a view context.
|
8
8
|
# In order to use this module, all you need is to implement
|
@@ -10,8 +10,8 @@ module ActionView
|
|
10
10
|
module RenderingHelper
|
11
11
|
# Returns the result of a render that's dictated by the options hash. The primary options are:
|
12
12
|
#
|
13
|
-
# * <tt>:partial</tt> - See
|
14
|
-
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add
|
13
|
+
# * <tt>:partial</tt> - See ActionView::PartialRenderer.
|
14
|
+
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add +:locals+ to pass in those.
|
15
15
|
# * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
|
16
16
|
# * <tt>:plain</tt> - Renders the text passed in out. Setting the content
|
17
17
|
# type as <tt>text/plain</tt>.
|
@@ -19,8 +19,7 @@ module ActionView
|
|
19
19
|
# performs HTML escape on the string first. Setting the content type as
|
20
20
|
# <tt>text/html</tt>.
|
21
21
|
# * <tt>:body</tt> - Renders the text passed in, and inherits the content
|
22
|
-
# type of <tt>text/plain</tt> from
|
23
|
-
# object.
|
22
|
+
# type of <tt>text/plain</tt> from ActionDispatch::Response object.
|
24
23
|
#
|
25
24
|
# If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, then:
|
26
25
|
#
|
@@ -47,7 +46,7 @@ module ActionView
|
|
47
46
|
end
|
48
47
|
end
|
49
48
|
|
50
|
-
#
|
49
|
+
# Overrides _layout_for in the context object so it supports the case a block is
|
51
50
|
# passed to a partial. Returns the contents that are yielded to a layout, given a
|
52
51
|
# name or a block.
|
53
52
|
#
|
@@ -3,20 +3,23 @@
|
|
3
3
|
require "rails-html-sanitizer"
|
4
4
|
|
5
5
|
module ActionView
|
6
|
-
# = Action View Sanitize Helpers
|
7
6
|
module Helpers # :nodoc:
|
7
|
+
# = Action View Sanitize \Helpers
|
8
|
+
#
|
8
9
|
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
|
9
10
|
# These helper methods extend Action View making them callable within your template files.
|
10
11
|
module SanitizeHelper
|
12
|
+
mattr_accessor :sanitizer_vendor, default: Rails::HTML4::Sanitizer
|
13
|
+
|
11
14
|
extend ActiveSupport::Concern
|
15
|
+
|
12
16
|
# Sanitizes HTML input, stripping all but known-safe tags and attributes.
|
13
17
|
#
|
14
|
-
# It also strips href/src attributes with unsafe protocols like
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# All special characters will be escaped.
|
18
|
+
# It also strips href/src attributes with unsafe protocols like <tt>javascript:</tt>, while
|
19
|
+
# also protecting against attempts to use Unicode, ASCII, and hex character references to work
|
20
|
+
# around these protocol filters.
|
18
21
|
#
|
19
|
-
# The default sanitizer is Rails::
|
22
|
+
# The default sanitizer is Rails::HTML5::SafeListSanitizer. See {Rails HTML
|
20
23
|
# Sanitizers}[https://github.com/rails/rails-html-sanitizer] for more information.
|
21
24
|
#
|
22
25
|
# Custom sanitization rules can also be provided.
|
@@ -28,7 +31,7 @@ module ActionView
|
|
28
31
|
#
|
29
32
|
# * <tt>:tags</tt> - An array of allowed tags.
|
30
33
|
# * <tt>:attributes</tt> - An array of allowed attributes.
|
31
|
-
# * <tt>:scrubber</tt> - A {Rails::
|
34
|
+
# * <tt>:scrubber</tt> - A {Rails::HTML scrubber}[https://github.com/rails/rails-html-sanitizer]
|
32
35
|
# or {Loofah::Scrubber}[https://github.com/flavorjones/loofah] object that
|
33
36
|
# defines custom sanitization rules. A custom scrubber takes precedence over
|
34
37
|
# custom tags and attributes.
|
@@ -43,9 +46,9 @@ module ActionView
|
|
43
46
|
#
|
44
47
|
# <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %>
|
45
48
|
#
|
46
|
-
# Providing a custom Rails::
|
49
|
+
# Providing a custom Rails::HTML scrubber:
|
47
50
|
#
|
48
|
-
# class CommentScrubber < Rails::
|
51
|
+
# class CommentScrubber < Rails::HTML::PermitScrubber
|
49
52
|
# def initialize
|
50
53
|
# super
|
51
54
|
# self.tags = %w( form script comment blockquote )
|
@@ -60,7 +63,7 @@ module ActionView
|
|
60
63
|
# <%= sanitize @comment.body, scrubber: CommentScrubber.new %>
|
61
64
|
#
|
62
65
|
# See {Rails HTML Sanitizer}[https://github.com/rails/rails-html-sanitizer] for
|
63
|
-
# documentation about Rails::
|
66
|
+
# documentation about Rails::HTML scrubbers.
|
64
67
|
#
|
65
68
|
# Providing a custom Loofah::Scrubber:
|
66
69
|
#
|
@@ -78,6 +81,22 @@ module ActionView
|
|
78
81
|
# # In config/application.rb
|
79
82
|
# config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a']
|
80
83
|
# config.action_view.sanitized_allowed_attributes = ['href', 'title']
|
84
|
+
#
|
85
|
+
# The default, starting in \Rails 7.1, is to use an HTML5 parser for sanitization (if it is
|
86
|
+
# available, see NOTE below). If you wish to revert back to the previous HTML4 behavior, you
|
87
|
+
# can do so by setting the following in your application configuration:
|
88
|
+
#
|
89
|
+
# # In config/application.rb
|
90
|
+
# config.action_view.sanitizer_vendor = Rails::HTML4::Sanitizer
|
91
|
+
#
|
92
|
+
# Or, if you're upgrading from a previous version of \Rails and wish to opt into the HTML5
|
93
|
+
# behavior:
|
94
|
+
#
|
95
|
+
# # In config/application.rb
|
96
|
+
# config.action_view.sanitizer_vendor = Rails::HTML5::Sanitizer
|
97
|
+
#
|
98
|
+
# NOTE: Rails::HTML5::Sanitizer is not supported on JRuby, so on JRuby platforms \Rails will
|
99
|
+
# fall back to use Rails::HTML4::Sanitizer.
|
81
100
|
def sanitize(html, options = {})
|
82
101
|
self.class.safe_list_sanitizer.sanitize(html, options)&.html_safe
|
83
102
|
end
|
@@ -101,7 +120,7 @@ module ActionView
|
|
101
120
|
# strip_tags("> A quote from Smith & Wesson")
|
102
121
|
# # => > A quote from Smith & Wesson
|
103
122
|
def strip_tags(html)
|
104
|
-
self.class.full_sanitizer.sanitize(html)
|
123
|
+
self.class.full_sanitizer.sanitize(html)&.html_safe
|
105
124
|
end
|
106
125
|
|
107
126
|
# Strips all link tags from +html+ leaving just the link text.
|
@@ -125,7 +144,7 @@ module ActionView
|
|
125
144
|
attr_writer :full_sanitizer, :link_sanitizer, :safe_list_sanitizer
|
126
145
|
|
127
146
|
def sanitizer_vendor
|
128
|
-
|
147
|
+
ActionView::Helpers::SanitizeHelper.sanitizer_vendor
|
129
148
|
end
|
130
149
|
|
131
150
|
def sanitized_allowed_tags
|
@@ -136,7 +155,7 @@ module ActionView
|
|
136
155
|
sanitizer_vendor.safe_list_sanitizer.allowed_attributes
|
137
156
|
end
|
138
157
|
|
139
|
-
# Gets the Rails::
|
158
|
+
# Gets the Rails::HTML::FullSanitizer instance used by +strip_tags+. Replace with
|
140
159
|
# any object that responds to +sanitize+.
|
141
160
|
#
|
142
161
|
# class Application < Rails::Application
|
@@ -146,7 +165,7 @@ module ActionView
|
|
146
165
|
@full_sanitizer ||= sanitizer_vendor.full_sanitizer.new
|
147
166
|
end
|
148
167
|
|
149
|
-
# Gets the Rails::
|
168
|
+
# Gets the Rails::HTML::LinkSanitizer instance used by +strip_links+.
|
150
169
|
# Replace with any object that responds to +sanitize+.
|
151
170
|
#
|
152
171
|
# class Application < Rails::Application
|
@@ -156,7 +175,7 @@ module ActionView
|
|
156
175
|
@link_sanitizer ||= sanitizer_vendor.link_sanitizer.new
|
157
176
|
end
|
158
177
|
|
159
|
-
# Gets the Rails::
|
178
|
+
# Gets the Rails::HTML::SafeListSanitizer instance used by sanitize and +sanitize_css+.
|
160
179
|
# Replace with any object that responds to +sanitize+.
|
161
180
|
#
|
162
181
|
# class Application < Rails::Application
|
@@ -7,8 +7,9 @@ require "action_view/helpers/capture_helper"
|
|
7
7
|
require "action_view/helpers/output_safety_helper"
|
8
8
|
|
9
9
|
module ActionView
|
10
|
-
# = Action View Tag Helpers
|
11
10
|
module Helpers # :nodoc:
|
11
|
+
# = Action View Tag \Helpers
|
12
|
+
#
|
12
13
|
# Provides methods to generate HTML tags programmatically both as a modern
|
13
14
|
# HTML5 compliant builder style and legacy XHTML compliant tags.
|
14
15
|
module TagHelper
|
@@ -45,8 +46,8 @@ module ActionView
|
|
45
46
|
include CaptureHelper
|
46
47
|
include OutputSafetyHelper
|
47
48
|
|
48
|
-
HTML_VOID_ELEMENTS = %i(area base br col
|
49
|
-
|
49
|
+
HTML_VOID_ELEMENTS = %i(area base br col embed hr img input keygen link meta param source track wbr).to_set
|
50
|
+
SVG_SELF_CLOSING_ELEMENTS = %i(animate animateMotion animateTransform circle ellipse line path polygon polyline rect set stop use view).to_set
|
50
51
|
|
51
52
|
def initialize(view_context)
|
52
53
|
@view_context = view_context
|
@@ -65,18 +66,24 @@ module ActionView
|
|
65
66
|
tag_string(:p, *arguments, **options, &block)
|
66
67
|
end
|
67
68
|
|
68
|
-
def tag_string(name, content = nil,
|
69
|
+
def tag_string(name, content = nil, escape: true, **options, &block)
|
69
70
|
content = @view_context.capture(self, &block) if block_given?
|
70
|
-
|
71
|
-
|
71
|
+
self_closing = SVG_SELF_CLOSING_ELEMENTS.include?(name)
|
72
|
+
if (HTML_VOID_ELEMENTS.include?(name) || self_closing) && content.nil?
|
73
|
+
"<#{name.to_s.dasherize}#{tag_options(options, escape)}#{self_closing ? " />" : ">"}".html_safe
|
72
74
|
else
|
73
|
-
content_tag_string(name.to_s.dasherize, content || "", options,
|
75
|
+
content_tag_string(name.to_s.dasherize, content || "", options, escape)
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
77
79
|
def content_tag_string(name, content, options, escape = true)
|
78
80
|
tag_options = tag_options(options, escape) if options
|
79
|
-
|
81
|
+
|
82
|
+
if escape
|
83
|
+
name = ERB::Util.xml_name_escape(name)
|
84
|
+
content = ERB::Util.unwrapped_html_escape(content)
|
85
|
+
end
|
86
|
+
|
80
87
|
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
|
81
88
|
end
|
82
89
|
|
@@ -127,6 +134,8 @@ module ActionView
|
|
127
134
|
end
|
128
135
|
|
129
136
|
def tag_option(key, value, escape)
|
137
|
+
key = ERB::Util.xml_name_escape(key) if escape
|
138
|
+
|
130
139
|
case value
|
131
140
|
when Array, Hash
|
132
141
|
value = TagHelper.build_tag_values(value) if key.to_s == "class"
|
@@ -137,6 +146,7 @@ module ActionView
|
|
137
146
|
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
|
138
147
|
end
|
139
148
|
value = value.gsub('"', """) if value.include?('"')
|
149
|
+
|
140
150
|
%(#{key}="#{value}")
|
141
151
|
end
|
142
152
|
|
@@ -208,7 +218,7 @@ module ActionView
|
|
208
218
|
#
|
209
219
|
# Thus <tt>data-user-id</tt> can be accessed as <tt>dataset.userId</tt>.
|
210
220
|
#
|
211
|
-
# Data attribute values are encoded to JSON, with the exception of strings, symbols and
|
221
|
+
# Data attribute values are encoded to JSON, with the exception of strings, symbols, and
|
212
222
|
# BigDecimals.
|
213
223
|
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
|
214
224
|
# from 1.4.3.
|
@@ -216,13 +226,13 @@ module ActionView
|
|
216
226
|
# tag.div data: { city_state: %w( Chicago IL ) }
|
217
227
|
# # => <div data-city-state="["Chicago","IL"]"></div>
|
218
228
|
#
|
219
|
-
# The generated attributes are escaped by default. This can be disabled using
|
220
|
-
# +
|
229
|
+
# The generated tag names and attributes are escaped by default. This can be disabled using
|
230
|
+
# +escape+.
|
221
231
|
#
|
222
232
|
# tag.img src: 'open & shut.png'
|
223
233
|
# # => <img src="open & shut.png">
|
224
234
|
#
|
225
|
-
# tag.img src: 'open & shut.png',
|
235
|
+
# tag.img src: 'open & shut.png', escape: false
|
226
236
|
# # => <img src="open & shut.png">
|
227
237
|
#
|
228
238
|
# The tag builder respects
|
@@ -240,7 +250,7 @@ module ActionView
|
|
240
250
|
# Transforms a Hash into HTML attributes, ready to be interpolated into
|
241
251
|
# ERB. Includes or omits boolean attributes based on their truthiness.
|
242
252
|
# Transforms keys nested within
|
243
|
-
# <tt>aria:</tt> or <tt>data:</tt> objects into
|
253
|
+
# <tt>aria:</tt> or <tt>data:</tt> objects into <tt>aria-</tt> and <tt>data-</tt>
|
244
254
|
# prefixed attributes:
|
245
255
|
#
|
246
256
|
# <input <%= tag.attributes(type: :text, aria: { label: "Search" }) %>>
|
@@ -251,7 +261,7 @@ module ActionView
|
|
251
261
|
#
|
252
262
|
# === Legacy syntax
|
253
263
|
#
|
254
|
-
# The following format is for legacy syntax support. It will be deprecated in future versions of Rails.
|
264
|
+
# The following format is for legacy syntax support. It will be deprecated in future versions of \Rails.
|
255
265
|
#
|
256
266
|
# tag(name, options = nil, open = false, escape = true)
|
257
267
|
#
|
@@ -300,6 +310,7 @@ module ActionView
|
|
300
310
|
if name.nil?
|
301
311
|
tag_builder
|
302
312
|
else
|
313
|
+
name = ERB::Util.xml_name_escape(name) if escape
|
303
314
|
"<#{name}#{tag_builder.tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
|
304
315
|
end
|
305
316
|
end
|
@@ -308,7 +319,7 @@ module ActionView
|
|
308
319
|
# HTML attributes by passing an attributes hash to +options+.
|
309
320
|
# Instead of passing the content as an argument, you can also use a block
|
310
321
|
# in which case, you pass your +options+ as the second parameter.
|
311
|
-
# Set escape to false to disable
|
322
|
+
# Set escape to false to disable escaping.
|
312
323
|
# Note: this is legacy syntax, see +tag+ method description for details.
|
313
324
|
#
|
314
325
|
# ==== Options
|
@@ -353,7 +364,7 @@ module ActionView
|
|
353
364
|
# token_list(nil, false, 123, "", "foo", { bar: true })
|
354
365
|
# # => "123 foo bar"
|
355
366
|
def token_list(*args)
|
356
|
-
tokens = build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq
|
367
|
+
tokens = build_tag_values(*args).flat_map { |value| CGI.unescape_html(value.to_s).split(/\s+/) }.uniq
|
357
368
|
|
358
369
|
safe_join(tokens, " ")
|
359
370
|
end
|
@@ -5,7 +5,6 @@ module ActionView
|
|
5
5
|
module Tags # :nodoc:
|
6
6
|
class Base # :nodoc:
|
7
7
|
include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
|
8
|
-
include FormOptionsHelper
|
9
8
|
|
10
9
|
attr_reader :object
|
11
10
|
|
@@ -35,22 +34,24 @@ module ActionView
|
|
35
34
|
|
36
35
|
private
|
37
36
|
def value
|
37
|
+
return unless object
|
38
|
+
|
38
39
|
if @allow_method_names_outside_object
|
39
|
-
object.public_send @method_name if object
|
40
|
+
object.public_send @method_name if object.respond_to?(@method_name)
|
40
41
|
else
|
41
|
-
object.public_send @method_name
|
42
|
+
object.public_send @method_name
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
46
|
def value_before_type_cast
|
46
|
-
unless object
|
47
|
-
method_before_type_cast = @method_name + "_before_type_cast"
|
47
|
+
return unless object
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
method_before_type_cast = @method_name + "_before_type_cast"
|
50
|
+
|
51
|
+
if value_came_from_user? && object.respond_to?(method_before_type_cast)
|
52
|
+
object.public_send(method_before_type_cast)
|
53
|
+
else
|
54
|
+
value
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
@@ -120,48 +121,6 @@ module ActionView
|
|
120
121
|
value.to_s.gsub(/[\s.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
|
121
122
|
end
|
122
123
|
|
123
|
-
def select_content_tag(option_tags, options, html_options)
|
124
|
-
html_options = html_options.stringify_keys
|
125
|
-
add_default_name_and_id(html_options)
|
126
|
-
|
127
|
-
if placeholder_required?(html_options)
|
128
|
-
raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
|
129
|
-
options[:include_blank] ||= true unless options[:prompt]
|
130
|
-
end
|
131
|
-
|
132
|
-
value = options.fetch(:selected) { value() }
|
133
|
-
select = content_tag("select", add_options(option_tags, options, value), html_options)
|
134
|
-
|
135
|
-
if html_options["multiple"] && options.fetch(:include_hidden, true)
|
136
|
-
tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "", autocomplete: "off") + select
|
137
|
-
else
|
138
|
-
select
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def placeholder_required?(html_options)
|
143
|
-
# See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
|
144
|
-
html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
|
145
|
-
end
|
146
|
-
|
147
|
-
def add_options(option_tags, options, value = nil)
|
148
|
-
if options[:include_blank]
|
149
|
-
content = (options[:include_blank] if options[:include_blank].is_a?(String))
|
150
|
-
label = (" " unless content)
|
151
|
-
option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
|
152
|
-
end
|
153
|
-
|
154
|
-
if value.blank? && options[:prompt]
|
155
|
-
tag_options = { value: "" }.tap do |prompt_opts|
|
156
|
-
prompt_opts[:disabled] = true if options[:disabled] == ""
|
157
|
-
prompt_opts[:selected] = true if options[:selected] == ""
|
158
|
-
end
|
159
|
-
option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
|
160
|
-
end
|
161
|
-
|
162
|
-
option_tags
|
163
|
-
end
|
164
|
-
|
165
124
|
def name_and_id_index(options)
|
166
125
|
if options.key?("index")
|
167
126
|
options.delete("index") || ""
|
@@ -4,6 +4,9 @@ module ActionView
|
|
4
4
|
module Helpers
|
5
5
|
module Tags # :nodoc:
|
6
6
|
class CollectionSelect < Base # :nodoc:
|
7
|
+
include SelectRenderer
|
8
|
+
include FormOptionsHelper
|
9
|
+
|
7
10
|
def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
|
8
11
|
@collection = collection
|
9
12
|
@value_method = value_method
|
@@ -6,20 +6,28 @@ module ActionView
|
|
6
6
|
class DatetimeField < TextField # :nodoc:
|
7
7
|
def render
|
8
8
|
options = @options.stringify_keys
|
9
|
-
options["value"]
|
10
|
-
options["min"] =
|
11
|
-
options["max"] =
|
9
|
+
options["value"] = datetime_value(options["value"] || value)
|
10
|
+
options["min"] = format_datetime(parse_datetime(options["min"]))
|
11
|
+
options["max"] = format_datetime(parse_datetime(options["max"]))
|
12
12
|
@options = options
|
13
13
|
super
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
|
-
def
|
17
|
+
def datetime_value(value)
|
18
|
+
if value.is_a?(String)
|
19
|
+
value
|
20
|
+
else
|
21
|
+
format_datetime(value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def format_datetime(value)
|
18
26
|
raise NotImplementedError
|
19
27
|
end
|
20
28
|
|
21
|
-
def
|
22
|
-
if value.is_a?
|
29
|
+
def parse_datetime(value)
|
30
|
+
if value.is_a?(String)
|
23
31
|
DateTime.parse(value) rescue nil
|
24
32
|
else
|
25
33
|
value
|
@@ -4,6 +4,11 @@ module ActionView
|
|
4
4
|
module Helpers
|
5
5
|
module Tags # :nodoc:
|
6
6
|
class DatetimeLocalField < DatetimeField # :nodoc:
|
7
|
+
def initialize(object_name, method_name, template_object, options = {})
|
8
|
+
@include_seconds = options.delete(:include_seconds) { true }
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
7
12
|
class << self
|
8
13
|
def field_type
|
9
14
|
@field_type ||= "datetime-local"
|
@@ -11,8 +16,12 @@ module ActionView
|
|
11
16
|
end
|
12
17
|
|
13
18
|
private
|
14
|
-
def
|
15
|
-
|
19
|
+
def format_datetime(value)
|
20
|
+
if @include_seconds
|
21
|
+
value&.strftime("%Y-%m-%dT%T")
|
22
|
+
else
|
23
|
+
value&.strftime("%Y-%m-%dT%H:%M")
|
24
|
+
end
|
16
25
|
end
|
17
26
|
end
|
18
27
|
end
|
@@ -4,6 +4,9 @@ module ActionView
|
|
4
4
|
module Helpers
|
5
5
|
module Tags # :nodoc:
|
6
6
|
class GroupedCollectionSelect < Base # :nodoc:
|
7
|
+
include SelectRenderer
|
8
|
+
include FormOptionsHelper
|
9
|
+
|
7
10
|
def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
8
11
|
@collection = collection
|
9
12
|
@group_method = group_method
|
@@ -4,6 +4,9 @@ module ActionView
|
|
4
4
|
module Helpers
|
5
5
|
module Tags # :nodoc:
|
6
6
|
class Select < Base # :nodoc:
|
7
|
+
include SelectRenderer
|
8
|
+
include FormOptionsHelper
|
9
|
+
|
7
10
|
def initialize(object_name, method_name, template_object, choices, options, html_options)
|
8
11
|
@choices = block_given? ? template_object.capture { yield || "" } : choices
|
9
12
|
@choices = @choices.to_a if @choices.is_a?(Range)
|
@@ -34,7 +37,7 @@ module ActionView
|
|
34
37
|
# [nil, []]
|
35
38
|
# { nil => [] }
|
36
39
|
def grouped_choices?
|
37
|
-
!@choices.blank? && @choices.first.respond_to?(:
|
40
|
+
!@choices.blank? && @choices.first.respond_to?(:second) && Array === @choices.first.second
|
38
41
|
end
|
39
42
|
end
|
40
43
|
end
|