actionview 7.0.4 → 7.1.5.1
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 +343 -232
- data/MIT-LICENSE +1 -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 +34 -14
- 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 +4 -4
- data/lib/action_view/helpers/active_model_helper.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +136 -52
- data/lib/action_view/helpers/asset_url_helper.rb +6 -5
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
- data/lib/action_view/helpers/cache_helper.rb +7 -13
- data/lib/action_view/helpers/capture_helper.rb +32 -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 +67 -59
- data/lib/action_view/helpers/debug_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +56 -26
- data/lib/action_view/helpers/form_options_helper.rb +4 -1
- data/lib/action_view/helpers/form_tag_helper.rb +49 -15
- data/lib/action_view/helpers/javascript_helper.rb +1 -0
- data/lib/action_view/helpers/number_helper.rb +37 -329
- data/lib/action_view/helpers/output_safety_helper.rb +4 -4
- data/lib/action_view/helpers/rendering_helper.rb +1 -1
- data/lib/action_view/helpers/sanitize_helper.rb +51 -21
- data/lib/action_view/helpers/tag_helper.rb +5 -27
- 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 +156 -84
- data/lib/action_view/helpers/translation_helper.rb +3 -3
- data/lib/action_view/helpers/url_helper.rb +47 -18
- data/lib/action_view/helpers.rb +2 -0
- data/lib/action_view/layouts.rb +8 -6
- 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 +15 -8
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- 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 +2 -1
- 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 +22 -4
- data/lib/action_view/ripper_ast_parser.rb +6 -6
- data/lib/action_view/routing_url_for.rb +4 -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 +249 -54
- data/lib/action_view/template_path.rb +2 -0
- data/lib/action_view/test_case.rb +176 -21
- data/lib/action_view/unbound_template.rb +17 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +15 -24
- data/lib/action_view.rb +4 -1
- metadata +27 -28
- data/lib/assets/compiled/rails-ujs.js +0 -746
@@ -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 +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
|
@@ -125,7 +155,7 @@ module ActionView
|
|
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
|
@@ -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
|
@@ -65,9 +66,7 @@ module ActionView
|
|
65
66
|
tag_string(:p, *arguments, **options, &block)
|
66
67
|
end
|
67
68
|
|
68
|
-
def tag_string(name, content = nil, **options, &block)
|
69
|
-
escape = handle_deprecated_escape_options(options)
|
70
|
-
|
69
|
+
def tag_string(name, content = nil, escape: true, **options, &block)
|
71
70
|
content = @view_context.capture(self, &block) if block_given?
|
72
71
|
self_closing = SVG_SELF_CLOSING_ELEMENTS.include?(name)
|
73
72
|
if (HTML_VOID_ELEMENTS.include?(name) || self_closing) && content.nil?
|
@@ -164,27 +163,6 @@ module ActionView
|
|
164
163
|
true
|
165
164
|
end
|
166
165
|
|
167
|
-
def handle_deprecated_escape_options(options)
|
168
|
-
# The option :escape_attributes has been merged into the options hash to be
|
169
|
-
# able to warn when it is used, so we need to handle default values here.
|
170
|
-
escape_option_provided = options.has_key?(:escape)
|
171
|
-
escape_attributes_option_provided = options.has_key?(:escape_attributes)
|
172
|
-
|
173
|
-
if escape_attributes_option_provided
|
174
|
-
ActiveSupport::Deprecation.warn(<<~MSG)
|
175
|
-
Use of the option :escape_attributes is deprecated. It currently \
|
176
|
-
escapes both names and values of tags and attributes and it is \
|
177
|
-
equivalent to :escape. If any of them are enabled, the escaping \
|
178
|
-
is fully enabled.
|
179
|
-
MSG
|
180
|
-
end
|
181
|
-
|
182
|
-
return true unless escape_option_provided || escape_attributes_option_provided
|
183
|
-
escape_option = options.delete(:escape)
|
184
|
-
escape_attributes_option = options.delete(:escape_attributes)
|
185
|
-
escape_option || escape_attributes_option
|
186
|
-
end
|
187
|
-
|
188
166
|
def method_missing(called, *args, **options, &block)
|
189
167
|
tag_string(called, *args, **options, &block)
|
190
168
|
end
|
@@ -283,7 +261,7 @@ module ActionView
|
|
283
261
|
#
|
284
262
|
# === Legacy syntax
|
285
263
|
#
|
286
|
-
# 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.
|
287
265
|
#
|
288
266
|
# tag(name, options = nil, open = false, escape = true)
|
289
267
|
#
|
@@ -386,7 +364,7 @@ module ActionView
|
|
386
364
|
# token_list(nil, false, 123, "", "foo", { bar: true })
|
387
365
|
# # => "123 foo bar"
|
388
366
|
def token_list(*args)
|
389
|
-
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
|
390
368
|
|
391
369
|
safe_join(tokens, " ")
|
392
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
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
module Helpers
|
5
|
+
module Tags # :nodoc:
|
6
|
+
module SelectRenderer # :nodoc:
|
7
|
+
private
|
8
|
+
def select_content_tag(option_tags, options, html_options)
|
9
|
+
html_options = html_options.stringify_keys
|
10
|
+
[:required, :multiple, :size].each do |prop|
|
11
|
+
html_options[prop.to_s] = options.delete(prop) if options.key?(prop) && !html_options.key?(prop.to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
add_default_name_and_id(html_options)
|
15
|
+
|
16
|
+
if placeholder_required?(html_options)
|
17
|
+
raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
|
18
|
+
options[:include_blank] ||= true unless options[:prompt]
|
19
|
+
end
|
20
|
+
|
21
|
+
value = options.fetch(:selected) { value() }
|
22
|
+
select = content_tag("select", add_options(option_tags, options, value), html_options)
|
23
|
+
|
24
|
+
if html_options["multiple"] && options.fetch(:include_hidden, true)
|
25
|
+
tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "", autocomplete: "off") + select
|
26
|
+
else
|
27
|
+
select
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def placeholder_required?(html_options)
|
32
|
+
# See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
|
33
|
+
html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_options(option_tags, options, value = nil)
|
37
|
+
if options[:include_blank]
|
38
|
+
content = (options[:include_blank] if options[:include_blank].is_a?(String))
|
39
|
+
label = (" " unless content)
|
40
|
+
option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
|
41
|
+
end
|
42
|
+
|
43
|
+
if value.blank? && options[:prompt]
|
44
|
+
tag_options = { value: "" }.tap do |prompt_opts|
|
45
|
+
prompt_opts[:disabled] = true if options[:disabled] == ""
|
46
|
+
prompt_opts[:selected] = true if options[:selected] == ""
|
47
|
+
end
|
48
|
+
option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
|
49
|
+
end
|
50
|
+
|
51
|
+
option_tags
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -4,6 +4,9 @@ module ActionView
|
|
4
4
|
module Helpers
|
5
5
|
module Tags # :nodoc:
|
6
6
|
class TimeZoneSelect < Base # :nodoc:
|
7
|
+
include SelectRenderer
|
8
|
+
include FormOptionsHelper
|
9
|
+
|
7
10
|
def initialize(object_name, method_name, template_object, priority_zones, options, html_options)
|
8
11
|
@priority_zones = priority_zones
|
9
12
|
@html_options = html_options
|
@@ -4,6 +4,9 @@ module ActionView
|
|
4
4
|
module Helpers
|
5
5
|
module Tags # :nodoc:
|
6
6
|
class WeekdaySelect < Base # :nodoc:
|
7
|
+
include SelectRenderer
|
8
|
+
include FormOptionsHelper
|
9
|
+
|
7
10
|
def initialize(object_name, method_name, template_object, options, html_options)
|
8
11
|
@html_options = html_options
|
9
12
|
|