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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +299 -277
- data/MIT-LICENSE +2 -1
- data/README.rdoc +3 -3
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +37 -19
- data/lib/action_view/buffers.rb +107 -9
- data/lib/action_view/cache_expiry.rb +48 -37
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
- data/lib/action_view/dependency_tracker.rb +6 -147
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +8 -5
- data/lib/action_view/flows.rb +4 -4
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/active_model_helper.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +200 -60
- data/lib/action_view/helpers/asset_url_helper.rb +22 -21
- data/lib/action_view/helpers/atom_feed_helper.rb +8 -9
- data/lib/action_view/helpers/cache_helper.rb +55 -12
- data/lib/action_view/helpers/capture_helper.rb +34 -14
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +8 -2
- data/lib/action_view/helpers/csp_helper.rb +3 -3
- data/lib/action_view/helpers/csrf_helper.rb +4 -4
- data/lib/action_view/helpers/date_helper.rb +123 -57
- data/lib/action_view/helpers/debug_helper.rb +6 -4
- data/lib/action_view/helpers/form_helper.rb +253 -97
- data/lib/action_view/helpers/form_options_helper.rb +72 -34
- data/lib/action_view/helpers/form_tag_helper.rb +189 -58
- data/lib/action_view/helpers/javascript_helper.rb +4 -5
- data/lib/action_view/helpers/number_helper.rb +43 -335
- data/lib/action_view/helpers/output_safety_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +6 -7
- data/lib/action_view/helpers/sanitize_helper.rb +54 -24
- data/lib/action_view/helpers/tag_helper.rb +42 -35
- data/lib/action_view/helpers/tags/base.rb +16 -77
- data/lib/action_view/helpers/tags/check_box.rb +1 -1
- 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 +4 -1
- 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/file_field.rb +16 -0
- 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 +11 -2
- 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 +31 -0
- data/lib/action_view/helpers/tags.rb +5 -2
- data/lib/action_view/helpers/text_helper.rb +180 -97
- data/lib/action_view/helpers/translation_helper.rb +14 -45
- data/lib/action_view/helpers/url_helper.rb +230 -132
- data/lib/action_view/helpers.rb +27 -25
- data/lib/action_view/layouts.rb +15 -10
- data/lib/action_view/log_subscriber.rb +49 -32
- data/lib/action_view/lookup_context.rb +58 -61
- data/lib/action_view/model_naming.rb +2 -2
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +28 -35
- data/lib/action_view/railtie.rb +44 -9
- data/lib/action_view/record_identifier.rb +16 -9
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +3 -3
- data/lib/action_view/renderer/collection_renderer.rb +10 -2
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
- data/lib/action_view/renderer/partial_renderer.rb +3 -36
- data/lib/action_view/renderer/renderer.rb +6 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +6 -5
- data/lib/action_view/renderer/template_renderer.rb +9 -4
- data/lib/action_view/rendering.rb +25 -7
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +8 -5
- data/lib/action_view/template/error.rb +122 -14
- 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 +79 -1
- data/lib/action_view/template/handlers.rb +4 -4
- data/lib/action_view/template/html.rb +4 -4
- data/lib/action_view/template/inline.rb +3 -3
- data/lib/action_view/template/raw_file.rb +4 -4
- data/lib/action_view/template/renderable.rb +1 -1
- data/lib/action_view/template/resolver.rb +96 -313
- data/lib/action_view/template/text.rb +4 -4
- data/lib/action_view/template/types.rb +25 -32
- data/lib/action_view/template.rb +245 -41
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +66 -0
- data/lib/action_view/test_case.rb +182 -23
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +43 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +19 -28
- data/lib/action_view.rb +6 -4
- data/lib/assets/compiled/rails-ujs.js +36 -5
- metadata +32 -25
|
@@ -3,20 +3,23 @@
|
|
|
3
3
|
require "rails-html-sanitizer"
|
|
4
4
|
|
|
5
5
|
module ActionView
|
|
6
|
-
|
|
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 +javascript:+, 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.
|
|
@@ -26,26 +29,31 @@ module ActionView
|
|
|
26
29
|
#
|
|
27
30
|
# ==== Options
|
|
28
31
|
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
+
# [+:tags+]
|
|
33
|
+
# An array of allowed tags.
|
|
34
|
+
#
|
|
35
|
+
# [+:attributes+]
|
|
36
|
+
# An array of allowed attributes.
|
|
37
|
+
#
|
|
38
|
+
# [+:scrubber+]
|
|
39
|
+
# A {Rails::HTML scrubber}[https://github.com/rails/rails-html-sanitizer]
|
|
32
40
|
# or {Loofah::Scrubber}[https://github.com/flavorjones/loofah] object that
|
|
33
41
|
# defines custom sanitization rules. A custom scrubber takes precedence over
|
|
34
42
|
# custom tags and attributes.
|
|
35
43
|
#
|
|
36
44
|
# ==== Examples
|
|
37
45
|
#
|
|
38
|
-
# Normal use
|
|
46
|
+
# ===== Normal use
|
|
39
47
|
#
|
|
40
48
|
# <%= sanitize @comment.body %>
|
|
41
49
|
#
|
|
42
|
-
# Providing custom lists of permitted tags and attributes
|
|
50
|
+
# ===== Providing custom lists of permitted tags and attributes
|
|
43
51
|
#
|
|
44
52
|
# <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %>
|
|
45
53
|
#
|
|
46
|
-
# Providing a custom Rails::
|
|
54
|
+
# ===== Providing a custom +Rails::HTML+ scrubber
|
|
47
55
|
#
|
|
48
|
-
# class CommentScrubber < Rails::
|
|
56
|
+
# class CommentScrubber < Rails::HTML::PermitScrubber
|
|
49
57
|
# def initialize
|
|
50
58
|
# super
|
|
51
59
|
# self.tags = %w( form script comment blockquote )
|
|
@@ -57,32 +65,54 @@ module ActionView
|
|
|
57
65
|
# end
|
|
58
66
|
# end
|
|
59
67
|
#
|
|
68
|
+
# <code></code>
|
|
69
|
+
#
|
|
60
70
|
# <%= sanitize @comment.body, scrubber: CommentScrubber.new %>
|
|
61
71
|
#
|
|
62
72
|
# See {Rails HTML Sanitizer}[https://github.com/rails/rails-html-sanitizer] for
|
|
63
|
-
# documentation about Rails::
|
|
73
|
+
# documentation about +Rails::HTML+ scrubbers.
|
|
64
74
|
#
|
|
65
|
-
# Providing a custom Loofah::Scrubber
|
|
75
|
+
# ===== Providing a custom +Loofah::Scrubber+
|
|
66
76
|
#
|
|
67
77
|
# scrubber = Loofah::Scrubber.new do |node|
|
|
68
78
|
# node.remove if node.name == 'script'
|
|
69
79
|
# end
|
|
70
80
|
#
|
|
81
|
+
# <code></code>
|
|
82
|
+
#
|
|
71
83
|
# <%= sanitize @comment.body, scrubber: scrubber %>
|
|
72
84
|
#
|
|
73
85
|
# See {Loofah's documentation}[https://github.com/flavorjones/loofah] for more
|
|
74
|
-
# information about defining custom Loofah::Scrubber objects.
|
|
86
|
+
# information about defining custom +Loofah::Scrubber+ objects.
|
|
87
|
+
#
|
|
88
|
+
# ==== Global Configuration
|
|
75
89
|
#
|
|
76
90
|
# To set the default allowed tags or attributes across your application:
|
|
77
91
|
#
|
|
78
92
|
# # In config/application.rb
|
|
79
93
|
# config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a']
|
|
80
94
|
# config.action_view.sanitized_allowed_attributes = ['href', 'title']
|
|
95
|
+
#
|
|
96
|
+
# The default, starting in \Rails 7.1, is to use an HTML5 parser for sanitization (if it is
|
|
97
|
+
# available, see NOTE below). If you wish to revert back to the previous HTML4 behavior, you
|
|
98
|
+
# can do so by setting the following in your application configuration:
|
|
99
|
+
#
|
|
100
|
+
# # In config/application.rb
|
|
101
|
+
# config.action_view.sanitizer_vendor = Rails::HTML4::Sanitizer
|
|
102
|
+
#
|
|
103
|
+
# Or, if you're upgrading from a previous version of \Rails and wish to opt into the HTML5
|
|
104
|
+
# behavior:
|
|
105
|
+
#
|
|
106
|
+
# # In config/application.rb
|
|
107
|
+
# config.action_view.sanitizer_vendor = Rails::HTML5::Sanitizer
|
|
108
|
+
#
|
|
109
|
+
# NOTE: +Rails::HTML5::Sanitizer+ is not supported on JRuby, so on JRuby platforms \Rails will
|
|
110
|
+
# fall back to using +Rails::HTML4::Sanitizer+.
|
|
81
111
|
def sanitize(html, options = {})
|
|
82
112
|
self.class.safe_list_sanitizer.sanitize(html, options)&.html_safe
|
|
83
113
|
end
|
|
84
114
|
|
|
85
|
-
# Sanitizes a block of CSS code. Used by
|
|
115
|
+
# Sanitizes a block of CSS code. Used by #sanitize when it comes across a style attribute.
|
|
86
116
|
def sanitize_css(style)
|
|
87
117
|
self.class.safe_list_sanitizer.sanitize_css(style)
|
|
88
118
|
end
|
|
@@ -101,7 +131,7 @@ module ActionView
|
|
|
101
131
|
# strip_tags("> A quote from Smith & Wesson")
|
|
102
132
|
# # => > A quote from Smith & Wesson
|
|
103
133
|
def strip_tags(html)
|
|
104
|
-
self.class.full_sanitizer.sanitize(html)
|
|
134
|
+
self.class.full_sanitizer.sanitize(html)&.html_safe
|
|
105
135
|
end
|
|
106
136
|
|
|
107
137
|
# Strips all link tags from +html+ leaving just the link text.
|
|
@@ -121,11 +151,11 @@ module ActionView
|
|
|
121
151
|
self.class.link_sanitizer.sanitize(html)
|
|
122
152
|
end
|
|
123
153
|
|
|
124
|
-
module ClassMethods
|
|
154
|
+
module ClassMethods # :nodoc:
|
|
125
155
|
attr_writer :full_sanitizer, :link_sanitizer, :safe_list_sanitizer
|
|
126
156
|
|
|
127
157
|
def sanitizer_vendor
|
|
128
|
-
|
|
158
|
+
ActionView::Helpers::SanitizeHelper.sanitizer_vendor
|
|
129
159
|
end
|
|
130
160
|
|
|
131
161
|
def sanitized_allowed_tags
|
|
@@ -136,7 +166,7 @@ module ActionView
|
|
|
136
166
|
sanitizer_vendor.safe_list_sanitizer.allowed_attributes
|
|
137
167
|
end
|
|
138
168
|
|
|
139
|
-
# Gets the Rails::
|
|
169
|
+
# Gets the Rails::HTML::FullSanitizer instance used by +strip_tags+. Replace with
|
|
140
170
|
# any object that responds to +sanitize+.
|
|
141
171
|
#
|
|
142
172
|
# class Application < Rails::Application
|
|
@@ -146,7 +176,7 @@ module ActionView
|
|
|
146
176
|
@full_sanitizer ||= sanitizer_vendor.full_sanitizer.new
|
|
147
177
|
end
|
|
148
178
|
|
|
149
|
-
# Gets the Rails::
|
|
179
|
+
# Gets the Rails::HTML::LinkSanitizer instance used by +strip_links+.
|
|
150
180
|
# Replace with any object that responds to +sanitize+.
|
|
151
181
|
#
|
|
152
182
|
# class Application < Rails::Application
|
|
@@ -156,7 +186,7 @@ module ActionView
|
|
|
156
186
|
@link_sanitizer ||= sanitizer_vendor.link_sanitizer.new
|
|
157
187
|
end
|
|
158
188
|
|
|
159
|
-
# Gets the Rails::
|
|
189
|
+
# Gets the Rails::HTML::SafeListSanitizer instance used by sanitize and +sanitize_css+.
|
|
160
190
|
# Replace with any object that responds to +sanitize+.
|
|
161
191
|
#
|
|
162
192
|
# class Application < Rails::Application
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
|
3
4
|
require "active_support/core_ext/string/output_safety"
|
|
4
5
|
require "set"
|
|
6
|
+
require "action_view/helpers/capture_helper"
|
|
7
|
+
require "action_view/helpers/output_safety_helper"
|
|
5
8
|
|
|
6
9
|
module ActionView
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
module Helpers # :nodoc:
|
|
11
|
+
# = Action View Tag \Helpers
|
|
12
|
+
#
|
|
9
13
|
# Provides methods to generate HTML tags programmatically both as a modern
|
|
10
14
|
# HTML5 compliant builder style and legacy XHTML compliant tags.
|
|
11
15
|
module TagHelper
|
|
12
|
-
extend ActiveSupport::Concern
|
|
13
16
|
include CaptureHelper
|
|
14
17
|
include OutputSafetyHelper
|
|
15
18
|
|
|
@@ -39,26 +42,35 @@ module ActionView
|
|
|
39
42
|
PRE_CONTENT_STRINGS[:textarea] = "\n"
|
|
40
43
|
PRE_CONTENT_STRINGS["textarea"] = "\n"
|
|
41
44
|
|
|
42
|
-
class TagBuilder
|
|
45
|
+
class TagBuilder # :nodoc:
|
|
43
46
|
include CaptureHelper
|
|
44
47
|
include OutputSafetyHelper
|
|
45
48
|
|
|
46
|
-
|
|
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
|
|
47
51
|
|
|
48
52
|
def initialize(view_context)
|
|
49
53
|
@view_context = view_context
|
|
50
54
|
end
|
|
51
55
|
|
|
56
|
+
# Transforms a Hash into HTML Attributes, ready to be interpolated into
|
|
57
|
+
# ERB.
|
|
58
|
+
#
|
|
59
|
+
# <input <%= tag.attributes(type: :text, aria: { label: "Search" }) %> >
|
|
60
|
+
# # => <input type="text" aria-label="Search">
|
|
61
|
+
def attributes(attributes)
|
|
62
|
+
tag_options(attributes.to_h).to_s.strip.html_safe
|
|
63
|
+
end
|
|
64
|
+
|
|
52
65
|
def p(*arguments, **options, &block)
|
|
53
66
|
tag_string(:p, *arguments, **options, &block)
|
|
54
67
|
end
|
|
55
68
|
|
|
56
|
-
def tag_string(name, content = nil, **options, &block)
|
|
57
|
-
escape = handle_deprecated_escape_options(options)
|
|
58
|
-
|
|
69
|
+
def tag_string(name, content = nil, escape: true, **options, &block)
|
|
59
70
|
content = @view_context.capture(self, &block) if block_given?
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
62
74
|
else
|
|
63
75
|
content_tag_string(name.to_s.dasherize, content || "", options, escape)
|
|
64
76
|
end
|
|
@@ -128,6 +140,8 @@ module ActionView
|
|
|
128
140
|
when Array, Hash
|
|
129
141
|
value = TagHelper.build_tag_values(value) if key.to_s == "class"
|
|
130
142
|
value = escape ? safe_join(value, " ") : value.join(" ")
|
|
143
|
+
when Regexp
|
|
144
|
+
value = escape ? ERB::Util.unwrapped_html_escape(value.source) : value.source
|
|
131
145
|
else
|
|
132
146
|
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
|
|
133
147
|
end
|
|
@@ -149,27 +163,6 @@ module ActionView
|
|
|
149
163
|
true
|
|
150
164
|
end
|
|
151
165
|
|
|
152
|
-
def handle_deprecated_escape_options(options)
|
|
153
|
-
# The option :escape_attributes has been merged into the options hash to be
|
|
154
|
-
# able to warn when it is used, so we need to handle default values here.
|
|
155
|
-
escape_option_provided = options.has_key?(:escape)
|
|
156
|
-
escape_attributes_option_provided = options.has_key?(:escape_attributes)
|
|
157
|
-
|
|
158
|
-
if escape_attributes_option_provided
|
|
159
|
-
ActiveSupport::Deprecation.warn(<<~MSG)
|
|
160
|
-
Use of the option :escape_attributes is deprecated. It currently \
|
|
161
|
-
escapes both names and values of tags and attributes and it is \
|
|
162
|
-
equivalent to :escape. If any of them are enabled, the escaping \
|
|
163
|
-
is fully enabled.
|
|
164
|
-
MSG
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
return true unless escape_option_provided || escape_attributes_option_provided
|
|
168
|
-
escape_option = options.delete(:escape)
|
|
169
|
-
escape_attributes_option = options.delete(:escape_attributes)
|
|
170
|
-
escape_option || escape_attributes_option
|
|
171
|
-
end
|
|
172
|
-
|
|
173
166
|
def method_missing(called, *args, **options, &block)
|
|
174
167
|
tag_string(called, *args, **options, &block)
|
|
175
168
|
end
|
|
@@ -225,7 +218,7 @@ module ActionView
|
|
|
225
218
|
#
|
|
226
219
|
# Thus <tt>data-user-id</tt> can be accessed as <tt>dataset.userId</tt>.
|
|
227
220
|
#
|
|
228
|
-
# 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
|
|
229
222
|
# BigDecimals.
|
|
230
223
|
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
|
|
231
224
|
# from 1.4.3.
|
|
@@ -252,9 +245,23 @@ module ActionView
|
|
|
252
245
|
# # A void element:
|
|
253
246
|
# tag.br # => <br>
|
|
254
247
|
#
|
|
248
|
+
# === Building HTML attributes
|
|
249
|
+
#
|
|
250
|
+
# Transforms a Hash into HTML attributes, ready to be interpolated into
|
|
251
|
+
# ERB. Includes or omits boolean attributes based on their truthiness.
|
|
252
|
+
# Transforms keys nested within
|
|
253
|
+
# <tt>aria:</tt> or <tt>data:</tt> objects into <tt>aria-</tt> and <tt>data-</tt>
|
|
254
|
+
# prefixed attributes:
|
|
255
|
+
#
|
|
256
|
+
# <input <%= tag.attributes(type: :text, aria: { label: "Search" }) %>>
|
|
257
|
+
# # => <input type="text" aria-label="Search">
|
|
258
|
+
#
|
|
259
|
+
# <button <%= tag.attributes id: "call-to-action", disabled: false, aria: { expanded: false } %> class="primary">Get Started!</button>
|
|
260
|
+
# # => <button id="call-to-action" aria-expanded="false" class="primary">Get Started!</button>
|
|
261
|
+
#
|
|
255
262
|
# === Legacy syntax
|
|
256
263
|
#
|
|
257
|
-
# 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.
|
|
258
265
|
#
|
|
259
266
|
# tag(name, options = nil, open = false, escape = true)
|
|
260
267
|
#
|
|
@@ -357,7 +364,7 @@ module ActionView
|
|
|
357
364
|
# token_list(nil, false, 123, "", "foo", { bar: true })
|
|
358
365
|
# # => "123 foo bar"
|
|
359
366
|
def token_list(*args)
|
|
360
|
-
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
|
|
361
368
|
|
|
362
369
|
safe_join(tokens, " ")
|
|
363
370
|
end
|
|
@@ -377,7 +384,7 @@ module ActionView
|
|
|
377
384
|
# cdata_section("hello]]>world")
|
|
378
385
|
# # => <![CDATA[hello]]]]><![CDATA[>world]]>
|
|
379
386
|
def cdata_section(content)
|
|
380
|
-
splitted = content.to_s.gsub(/\]\]
|
|
387
|
+
splitted = content.to_s.gsub(/\]\]>/, "]]]]><![CDATA[>")
|
|
381
388
|
"<![CDATA[#{splitted}]]>".html_safe
|
|
382
389
|
end
|
|
383
390
|
|
|
@@ -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
|
|
|
@@ -97,7 +98,7 @@ module ActionView
|
|
|
97
98
|
options["name"] = options.fetch("name") { tag_name(options["multiple"], index) }
|
|
98
99
|
|
|
99
100
|
if generate_ids?
|
|
100
|
-
options["id"] = options.fetch("id") { tag_id(index) }
|
|
101
|
+
options["id"] = options.fetch("id") { tag_id(index, options.delete("namespace")) }
|
|
101
102
|
if namespace = options.delete("namespace")
|
|
102
103
|
options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
|
|
103
104
|
end
|
|
@@ -105,31 +106,11 @@ module ActionView
|
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
def tag_name(multiple = false, index = nil)
|
|
108
|
-
|
|
109
|
-
case
|
|
110
|
-
when @object_name.empty?
|
|
111
|
-
"#{sanitized_method_name}#{multiple ? "[]" : ""}"
|
|
112
|
-
when index
|
|
113
|
-
"#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
|
|
114
|
-
else
|
|
115
|
-
"#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def tag_id(index = nil)
|
|
120
|
-
# a little duplication to construct fewer strings
|
|
121
|
-
case
|
|
122
|
-
when @object_name.empty?
|
|
123
|
-
sanitized_method_name.dup
|
|
124
|
-
when index
|
|
125
|
-
"#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
|
|
126
|
-
else
|
|
127
|
-
"#{sanitized_object_name}_#{sanitized_method_name}"
|
|
128
|
-
end
|
|
109
|
+
@template_object.field_name(@object_name, sanitized_method_name, multiple: multiple, index: index)
|
|
129
110
|
end
|
|
130
111
|
|
|
131
|
-
def
|
|
132
|
-
@
|
|
112
|
+
def tag_id(index = nil, namespace = nil)
|
|
113
|
+
@template_object.field_id(@object_name, @method_name, index: index, namespace: namespace)
|
|
133
114
|
end
|
|
134
115
|
|
|
135
116
|
def sanitized_method_name
|
|
@@ -137,49 +118,7 @@ module ActionView
|
|
|
137
118
|
end
|
|
138
119
|
|
|
139
120
|
def sanitized_value(value)
|
|
140
|
-
value.to_s.gsub(/[\s
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def select_content_tag(option_tags, options, html_options)
|
|
144
|
-
html_options = html_options.stringify_keys
|
|
145
|
-
add_default_name_and_id(html_options)
|
|
146
|
-
|
|
147
|
-
if placeholder_required?(html_options)
|
|
148
|
-
raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
|
|
149
|
-
options[:include_blank] ||= true unless options[:prompt]
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
value = options.fetch(:selected) { value() }
|
|
153
|
-
select = content_tag("select", add_options(option_tags, options, value), html_options)
|
|
154
|
-
|
|
155
|
-
if html_options["multiple"] && options.fetch(:include_hidden, true)
|
|
156
|
-
tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "", autocomplete: "off") + select
|
|
157
|
-
else
|
|
158
|
-
select
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def placeholder_required?(html_options)
|
|
163
|
-
# See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
|
|
164
|
-
html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def add_options(option_tags, options, value = nil)
|
|
168
|
-
if options[:include_blank]
|
|
169
|
-
content = (options[:include_blank] if options[:include_blank].is_a?(String))
|
|
170
|
-
label = (" " unless content)
|
|
171
|
-
option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
if value.blank? && options[:prompt]
|
|
175
|
-
tag_options = { value: "" }.tap do |prompt_opts|
|
|
176
|
-
prompt_opts[:disabled] = true if options[:disabled] == ""
|
|
177
|
-
prompt_opts[:selected] = true if options[:selected] == ""
|
|
178
|
-
end
|
|
179
|
-
option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
option_tags
|
|
121
|
+
value.to_s.gsub(/[\s.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
|
|
183
122
|
end
|
|
184
123
|
|
|
185
124
|
def name_and_id_index(options)
|
|
@@ -5,7 +5,7 @@ require "action_view/helpers/tags/checkable"
|
|
|
5
5
|
module ActionView
|
|
6
6
|
module Helpers
|
|
7
7
|
module Tags # :nodoc:
|
|
8
|
-
class CheckBox < Base
|
|
8
|
+
class CheckBox < Base # :nodoc:
|
|
9
9
|
include Checkable
|
|
10
10
|
|
|
11
11
|
def initialize(object_name, method_name, template_object, checked_value, unchecked_value, options)
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
module ActionView
|
|
4
4
|
module Helpers
|
|
5
5
|
module Tags # :nodoc:
|
|
6
|
-
class CollectionSelect < Base
|
|
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,22 @@ module ActionView
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module Tags # :nodoc:
|
|
6
6
|
class FileField < TextField # :nodoc:
|
|
7
|
+
def render
|
|
8
|
+
include_hidden = @options.delete(:include_hidden)
|
|
9
|
+
options = @options.stringify_keys
|
|
10
|
+
add_default_name_and_id(options)
|
|
11
|
+
|
|
12
|
+
if options["multiple"] && include_hidden
|
|
13
|
+
hidden_field_for_multiple_file(options) + super
|
|
14
|
+
else
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
def hidden_field_for_multiple_file(options)
|
|
21
|
+
tag("input", "name" => options["name"], "type" => "hidden", "value" => "", "autocomplete" => "off")
|
|
22
|
+
end
|
|
7
23
|
end
|
|
8
24
|
end
|
|
9
25
|
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
|