actionview 5.1.4 → 6.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +199 -168
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -5
- data/lib/action_view.rb +10 -4
- data/lib/action_view/base.rb +87 -23
- data/lib/action_view/buffers.rb +17 -0
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +7 -11
- data/lib/action_view/dependency_tracker.rb +12 -4
- data/lib/action_view/digestor.rb +24 -23
- data/lib/action_view/flows.rb +2 -1
- data/lib/action_view/gem_version.rb +4 -2
- data/lib/action_view/helpers.rb +4 -2
- data/lib/action_view/helpers/active_model_helper.rb +9 -4
- data/lib/action_view/helpers/asset_tag_helper.rb +220 -57
- data/lib/action_view/helpers/asset_url_helper.rb +28 -23
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -2
- data/lib/action_view/helpers/cache_helper.rb +39 -28
- data/lib/action_view/helpers/capture_helper.rb +13 -7
- data/lib/action_view/helpers/controller_helper.rb +3 -1
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +5 -3
- data/lib/action_view/helpers/date_helper.rb +78 -33
- data/lib/action_view/helpers/debug_helper.rb +4 -2
- data/lib/action_view/helpers/form_helper.rb +357 -106
- data/lib/action_view/helpers/form_options_helper.rb +45 -39
- data/lib/action_view/helpers/form_tag_helper.rb +42 -27
- data/lib/action_view/helpers/javascript_helper.rb +28 -12
- data/lib/action_view/helpers/number_helper.rb +16 -8
- data/lib/action_view/helpers/output_safety_helper.rb +3 -1
- data/lib/action_view/helpers/rendering_helper.rb +20 -9
- data/lib/action_view/helpers/sanitize_helper.rb +15 -19
- data/lib/action_view/helpers/tag_helper.rb +100 -24
- data/lib/action_view/helpers/tags.rb +3 -1
- data/lib/action_view/helpers/tags/base.rb +30 -21
- data/lib/action_view/helpers/tags/check_box.rb +3 -2
- data/lib/action_view/helpers/tags/checkable.rb +4 -2
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +2 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +2 -1
- data/lib/action_view/helpers/tags/collection_select.rb +3 -1
- data/lib/action_view/helpers/tags/color_field.rb +4 -3
- data/lib/action_view/helpers/tags/date_field.rb +3 -2
- data/lib/action_view/helpers/tags/date_select.rb +5 -4
- data/lib/action_view/helpers/tags/datetime_field.rb +3 -2
- data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
- data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
- data/lib/action_view/helpers/tags/email_field.rb +2 -0
- data/lib/action_view/helpers/tags/file_field.rb +2 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -1
- data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
- data/lib/action_view/helpers/tags/label.rb +6 -5
- data/lib/action_view/helpers/tags/month_field.rb +3 -2
- data/lib/action_view/helpers/tags/number_field.rb +2 -0
- data/lib/action_view/helpers/tags/password_field.rb +2 -0
- data/lib/action_view/helpers/tags/placeholderable.rb +2 -0
- data/lib/action_view/helpers/tags/radio_button.rb +3 -2
- data/lib/action_view/helpers/tags/range_field.rb +2 -0
- data/lib/action_view/helpers/tags/search_field.rb +2 -0
- data/lib/action_view/helpers/tags/select.rb +4 -3
- data/lib/action_view/helpers/tags/tel_field.rb +2 -0
- data/lib/action_view/helpers/tags/text_area.rb +3 -1
- data/lib/action_view/helpers/tags/text_field.rb +3 -2
- data/lib/action_view/helpers/tags/time_field.rb +3 -2
- data/lib/action_view/helpers/tags/time_select.rb +2 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
- data/lib/action_view/helpers/tags/translator.rb +3 -6
- data/lib/action_view/helpers/tags/url_field.rb +2 -0
- data/lib/action_view/helpers/tags/week_field.rb +3 -2
- data/lib/action_view/helpers/text_helper.rb +11 -10
- data/lib/action_view/helpers/translation_helper.rb +102 -52
- data/lib/action_view/helpers/url_helper.rb +150 -32
- data/lib/action_view/layouts.rb +15 -15
- data/lib/action_view/log_subscriber.rb +32 -15
- data/lib/action_view/lookup_context.rb +67 -39
- data/lib/action_view/model_naming.rb +2 -0
- data/lib/action_view/path_set.rb +5 -12
- data/lib/action_view/railtie.rb +46 -21
- data/lib/action_view/record_identifier.rb +4 -3
- data/lib/action_view/renderer/abstract_renderer.rb +144 -11
- data/lib/action_view/renderer/collection_renderer.rb +196 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer.rb +33 -283
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +64 -17
- data/lib/action_view/renderer/renderer.rb +61 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +14 -8
- data/lib/action_view/renderer/template_renderer.rb +36 -26
- data/lib/action_view/rendering.rb +57 -38
- data/lib/action_view/routing_url_for.rb +15 -12
- data/lib/action_view/tasks/cache_digests.rake +2 -0
- data/lib/action_view/template.rb +69 -76
- data/lib/action_view/template/error.rb +32 -18
- data/lib/action_view/template/handlers.rb +4 -2
- data/lib/action_view/template/handlers/builder.rb +5 -6
- data/lib/action_view/template/handlers/erb.rb +20 -19
- data/lib/action_view/template/handlers/erb/erubi.rb +17 -9
- data/lib/action_view/template/handlers/html.rb +3 -1
- data/lib/action_view/template/handlers/raw.rb +4 -2
- data/lib/action_view/template/html.rb +8 -7
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +194 -152
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/text.rb +5 -4
- data/lib/action_view/template/types.rb +3 -1
- data/lib/action_view/test_case.rb +38 -30
- data/lib/action_view/testing/resolvers.rb +20 -27
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/version.rb +2 -0
- data/lib/action_view/view_paths.rb +61 -40
- data/lib/assets/compiled/rails-ujs.js +84 -23
- metadata +34 -23
- data/lib/action_view/helpers/record_tag_helper.rb +0 -21
- data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +0 -9
- data/lib/action_view/template/handlers/erb/erubis.rb +0 -81
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "action_view/helpers/tag_helper"
|
2
4
|
|
3
5
|
module ActionView
|
4
|
-
module Helpers
|
6
|
+
module Helpers #:nodoc:
|
5
7
|
module JavaScriptHelper
|
6
8
|
JS_ESCAPE_MAP = {
|
7
9
|
'\\' => '\\\\',
|
@@ -10,11 +12,13 @@ module ActionView
|
|
10
12
|
"\n" => '\n',
|
11
13
|
"\r" => '\n',
|
12
14
|
'"' => '\\"',
|
13
|
-
"'" => "\\'"
|
15
|
+
"'" => "\\'",
|
16
|
+
"`" => "\\`",
|
17
|
+
"$" => "\\$"
|
14
18
|
}
|
15
19
|
|
16
|
-
JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = "
"
|
17
|
-
JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = "
"
|
20
|
+
JS_ESCAPE_MAP[(+"\342\200\250").force_encoding(Encoding::UTF_8).encode!] = "
"
|
21
|
+
JS_ESCAPE_MAP[(+"\342\200\251").force_encoding(Encoding::UTF_8).encode!] = "
"
|
18
22
|
|
19
23
|
# Escapes carriage returns and single and double quotes for JavaScript segments.
|
20
24
|
#
|
@@ -23,12 +27,13 @@ module ActionView
|
|
23
27
|
#
|
24
28
|
# $('some_element').replaceWith('<%= j render 'some/element_template' %>');
|
25
29
|
def escape_javascript(javascript)
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
javascript = javascript.to_s
|
31
|
+
if javascript.empty?
|
32
|
+
result = ""
|
29
33
|
else
|
30
|
-
"
|
34
|
+
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
|
31
35
|
end
|
36
|
+
javascript.html_safe? ? result.html_safe : result
|
32
37
|
end
|
33
38
|
|
34
39
|
alias_method :j, :escape_javascript
|
@@ -46,10 +51,10 @@ module ActionView
|
|
46
51
|
# +html_options+ may be a hash of attributes for the <tt>\<script></tt>
|
47
52
|
# tag.
|
48
53
|
#
|
49
|
-
# javascript_tag "alert('All is good')",
|
54
|
+
# javascript_tag "alert('All is good')", type: 'application/javascript'
|
50
55
|
#
|
51
56
|
# Returns:
|
52
|
-
# <script
|
57
|
+
# <script type="application/javascript">
|
53
58
|
# //<![CDATA[
|
54
59
|
# alert('All is good')
|
55
60
|
# //]]>
|
@@ -58,7 +63,14 @@ module ActionView
|
|
58
63
|
# Instead of passing the content as an argument, you can also use a block
|
59
64
|
# in which case, you pass your +html_options+ as the first parameter.
|
60
65
|
#
|
61
|
-
# <%= javascript_tag
|
66
|
+
# <%= javascript_tag type: 'application/javascript' do -%>
|
67
|
+
# alert('All is good')
|
68
|
+
# <% end -%>
|
69
|
+
#
|
70
|
+
# If you have a content security policy enabled then you can add an automatic
|
71
|
+
# nonce value by passing <tt>nonce: true</tt> as part of +html_options+. Example:
|
72
|
+
#
|
73
|
+
# <%= javascript_tag nonce: true do -%>
|
62
74
|
# alert('All is good')
|
63
75
|
# <% end -%>
|
64
76
|
def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
|
@@ -70,7 +82,11 @@ module ActionView
|
|
70
82
|
content_or_options_with_block
|
71
83
|
end
|
72
84
|
|
73
|
-
|
85
|
+
if html_options[:nonce] == true
|
86
|
+
html_options[:nonce] = content_security_policy_nonce
|
87
|
+
end
|
88
|
+
|
89
|
+
content_tag("script", javascript_cdata_section(content), html_options)
|
74
90
|
end
|
75
91
|
|
76
92
|
def javascript_cdata_section(content) #:nodoc:
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/hash/keys"
|
2
4
|
require "active_support/core_ext/string/output_safety"
|
3
5
|
require "active_support/number_helper"
|
@@ -55,7 +57,7 @@ module ActionView
|
|
55
57
|
#
|
56
58
|
# number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
|
57
59
|
# # => "(755) 6123-4567"
|
58
|
-
# number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
|
60
|
+
# number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
|
59
61
|
# # => "133-1234-5678"
|
60
62
|
def number_to_phone(number, options = {})
|
61
63
|
return unless number
|
@@ -98,6 +100,9 @@ module ActionView
|
|
98
100
|
# absolute value of the number.
|
99
101
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
100
102
|
# the argument is invalid.
|
103
|
+
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
|
104
|
+
# insignificant zeros after the decimal separator (defaults to
|
105
|
+
# +false+).
|
101
106
|
#
|
102
107
|
# ==== Examples
|
103
108
|
#
|
@@ -109,12 +114,16 @@ module ActionView
|
|
109
114
|
#
|
110
115
|
# number_to_currency("123a456", raise: true) # => InvalidNumberError
|
111
116
|
#
|
117
|
+
# number_to_currency(-0.456789, precision: 0)
|
118
|
+
# # => "$0"
|
112
119
|
# number_to_currency(-1234567890.50, negative_format: "(%u%n)")
|
113
120
|
# # => ($1,234,567,890.50)
|
114
121
|
# number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
|
115
122
|
# # => R$1234567890,50
|
116
123
|
# number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
|
117
124
|
# # => 1234567890,50 R$
|
125
|
+
# number_to_currency(1234567890.50, strip_insignificant_zeros: true)
|
126
|
+
# # => "$1,234,567,890.5"
|
118
127
|
def number_to_currency(number, options = {})
|
119
128
|
delegate_number_helper_method(:number_to_currency, number, options)
|
120
129
|
end
|
@@ -244,7 +253,7 @@ module ActionView
|
|
244
253
|
end
|
245
254
|
|
246
255
|
# Formats the bytes in +number+ into a more understandable
|
247
|
-
# representation (e.g., giving it 1500 yields 1.
|
256
|
+
# representation (e.g., giving it 1500 yields 1.46 KB). This
|
248
257
|
# method is useful for reporting file sizes to users. You can
|
249
258
|
# customize the format in the +options+ hash.
|
250
259
|
#
|
@@ -290,7 +299,7 @@ module ActionView
|
|
290
299
|
end
|
291
300
|
|
292
301
|
# Pretty prints (formats and approximates) a number in a way it
|
293
|
-
# is more readable by humans (
|
302
|
+
# is more readable by humans (e.g.: 1200000000 becomes "1.2
|
294
303
|
# Billion"). This is useful for numbers that can get very large
|
295
304
|
# (and too hard to read).
|
296
305
|
#
|
@@ -298,7 +307,7 @@ module ActionView
|
|
298
307
|
# size.
|
299
308
|
#
|
300
309
|
# You can also define your own unit-quantifier names if you want
|
301
|
-
# to use other decimal units (
|
310
|
+
# to use other decimal units (e.g.: 1500 becomes "1.5
|
302
311
|
# kilometers", 0.150 becomes "150 milliliters", etc). You may
|
303
312
|
# define a wide range of unit quantifiers, even fractional ones
|
304
313
|
# (centi, deci, mili, etc).
|
@@ -396,7 +405,6 @@ module ActionView
|
|
396
405
|
end
|
397
406
|
|
398
407
|
private
|
399
|
-
|
400
408
|
def delegate_number_helper_method(method, number, options)
|
401
409
|
return unless number
|
402
410
|
options = escape_unsafe_options(options.symbolize_keys)
|
@@ -417,9 +425,9 @@ module ActionView
|
|
417
425
|
end
|
418
426
|
|
419
427
|
def escape_units(units)
|
420
|
-
|
421
|
-
|
422
|
-
end
|
428
|
+
units.transform_values do |v|
|
429
|
+
ERB::Util.html_escape(v)
|
430
|
+
end
|
423
431
|
end
|
424
432
|
|
425
433
|
def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/string/output_safety"
|
2
4
|
|
3
5
|
module ActionView #:nodoc:
|
@@ -36,7 +38,7 @@ module ActionView #:nodoc:
|
|
36
38
|
|
37
39
|
# Converts the array to a comma-separated sentence where the last element is
|
38
40
|
# joined by the connector word. This is the html_safe-aware version of
|
39
|
-
# ActiveSupport's {Array#to_sentence}[
|
41
|
+
# ActiveSupport's {Array#to_sentence}[https://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
|
40
42
|
#
|
41
43
|
def to_sentence(array, options = {})
|
42
44
|
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
|
-
module Helpers
|
4
|
+
module Helpers #:nodoc:
|
3
5
|
# = Action View Rendering
|
4
6
|
#
|
5
7
|
# Implements methods that allow rendering from a view context.
|
@@ -11,7 +13,6 @@ module ActionView
|
|
11
13
|
# * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt>.
|
12
14
|
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
|
13
15
|
# * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
|
14
|
-
# * <tt>:text</tt> - Renders the text passed in out.
|
15
16
|
# * <tt>:plain</tt> - Renders the text passed in out. Setting the content
|
16
17
|
# type as <tt>text/plain</tt>.
|
17
18
|
# * <tt>:html</tt> - Renders the HTML safe string passed in out, otherwise
|
@@ -21,18 +22,28 @@ module ActionView
|
|
21
22
|
# type of <tt>text/plain</tt> from <tt>ActionDispatch::Response</tt>
|
22
23
|
# object.
|
23
24
|
#
|
24
|
-
# If no options hash is passed or
|
25
|
-
#
|
25
|
+
# If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, then:
|
26
|
+
#
|
27
|
+
# If an object responding to `render_in` is passed, `render_in` is called on the object,
|
28
|
+
# passing in the current view context.
|
29
|
+
#
|
30
|
+
# Otherwise, a partial is rendered using the second parameter as the locals hash.
|
26
31
|
def render(options = {}, locals = {}, &block)
|
27
32
|
case options
|
28
33
|
when Hash
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
in_rendering_context(options) do |renderer|
|
35
|
+
if block_given?
|
36
|
+
view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
|
37
|
+
else
|
38
|
+
view_renderer.render(self, options)
|
39
|
+
end
|
33
40
|
end
|
34
41
|
else
|
35
|
-
|
42
|
+
if options.respond_to?(:render_in)
|
43
|
+
options.render_in(self, &block)
|
44
|
+
else
|
45
|
+
view_renderer.render_partial(self, partial: options, locals: locals, &block)
|
46
|
+
end
|
36
47
|
end
|
37
48
|
end
|
38
49
|
|
@@ -1,21 +1,22 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "rails-html-sanitizer"
|
3
4
|
|
4
5
|
module ActionView
|
5
6
|
# = Action View Sanitize Helpers
|
6
|
-
module Helpers
|
7
|
+
module Helpers #:nodoc:
|
7
8
|
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
|
8
9
|
# These helper methods extend Action View making them callable within your template files.
|
9
10
|
module SanitizeHelper
|
10
11
|
extend ActiveSupport::Concern
|
11
|
-
# Sanitizes HTML input, stripping all tags and attributes
|
12
|
+
# Sanitizes HTML input, stripping all but known-safe tags and attributes.
|
12
13
|
#
|
13
14
|
# It also strips href/src attributes with unsafe protocols like
|
14
15
|
# <tt>javascript:</tt>, while also protecting against attempts to use Unicode,
|
15
16
|
# ASCII, and hex character references to work around these protocol filters.
|
16
17
|
# All special characters will be escaped.
|
17
18
|
#
|
18
|
-
# The default sanitizer is Rails::Html::
|
19
|
+
# The default sanitizer is Rails::Html::SafeListSanitizer. See {Rails HTML
|
19
20
|
# Sanitizers}[https://github.com/rails/rails-html-sanitizer] for more information.
|
20
21
|
#
|
21
22
|
# Custom sanitization rules can also be provided.
|
@@ -38,7 +39,7 @@ module ActionView
|
|
38
39
|
#
|
39
40
|
# <%= sanitize @comment.body %>
|
40
41
|
#
|
41
|
-
# Providing custom
|
42
|
+
# Providing custom lists of permitted tags and attributes:
|
42
43
|
#
|
43
44
|
# <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %>
|
44
45
|
#
|
@@ -78,12 +79,12 @@ module ActionView
|
|
78
79
|
# config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a']
|
79
80
|
# config.action_view.sanitized_allowed_attributes = ['href', 'title']
|
80
81
|
def sanitize(html, options = {})
|
81
|
-
self.class.
|
82
|
+
self.class.safe_list_sanitizer.sanitize(html, options)&.html_safe
|
82
83
|
end
|
83
84
|
|
84
85
|
# Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
|
85
86
|
def sanitize_css(style)
|
86
|
-
self.class.
|
87
|
+
self.class.safe_list_sanitizer.sanitize_css(style)
|
87
88
|
end
|
88
89
|
|
89
90
|
# Strips all HTML tags from +html+, including comments and special characters.
|
@@ -121,20 +122,18 @@ module ActionView
|
|
121
122
|
end
|
122
123
|
|
123
124
|
module ClassMethods #:nodoc:
|
124
|
-
attr_writer :full_sanitizer, :link_sanitizer, :
|
125
|
+
attr_writer :full_sanitizer, :link_sanitizer, :safe_list_sanitizer
|
125
126
|
|
126
|
-
# Vendors the full, link and white list sanitizers.
|
127
|
-
# Provided strictly for compatibility and can be removed in Rails 5.1.
|
128
127
|
def sanitizer_vendor
|
129
128
|
Rails::Html::Sanitizer
|
130
129
|
end
|
131
130
|
|
132
131
|
def sanitized_allowed_tags
|
133
|
-
sanitizer_vendor.
|
132
|
+
sanitizer_vendor.safe_list_sanitizer.allowed_tags
|
134
133
|
end
|
135
134
|
|
136
135
|
def sanitized_allowed_attributes
|
137
|
-
sanitizer_vendor.
|
136
|
+
sanitizer_vendor.safe_list_sanitizer.allowed_attributes
|
138
137
|
end
|
139
138
|
|
140
139
|
# Gets the Rails::Html::FullSanitizer instance used by +strip_tags+. Replace with
|
@@ -143,7 +142,6 @@ module ActionView
|
|
143
142
|
# class Application < Rails::Application
|
144
143
|
# config.action_view.full_sanitizer = MySpecialSanitizer.new
|
145
144
|
# end
|
146
|
-
#
|
147
145
|
def full_sanitizer
|
148
146
|
@full_sanitizer ||= sanitizer_vendor.full_sanitizer.new
|
149
147
|
end
|
@@ -154,20 +152,18 @@ module ActionView
|
|
154
152
|
# class Application < Rails::Application
|
155
153
|
# config.action_view.link_sanitizer = MySpecialSanitizer.new
|
156
154
|
# end
|
157
|
-
#
|
158
155
|
def link_sanitizer
|
159
156
|
@link_sanitizer ||= sanitizer_vendor.link_sanitizer.new
|
160
157
|
end
|
161
158
|
|
162
|
-
# Gets the Rails::Html::
|
159
|
+
# Gets the Rails::Html::SafeListSanitizer instance used by sanitize and +sanitize_css+.
|
163
160
|
# Replace with any object that responds to +sanitize+.
|
164
161
|
#
|
165
162
|
# class Application < Rails::Application
|
166
|
-
# config.action_view.
|
163
|
+
# config.action_view.safe_list_sanitizer = MySpecialSanitizer.new
|
167
164
|
# end
|
168
|
-
|
169
|
-
|
170
|
-
@white_list_sanitizer ||= sanitizer_vendor.white_list_sanitizer.new
|
165
|
+
def safe_list_sanitizer
|
166
|
+
@safe_list_sanitizer ||= sanitizer_vendor.safe_list_sanitizer.new
|
171
167
|
end
|
172
168
|
end
|
173
169
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/string/output_safety"
|
4
4
|
require "set"
|
@@ -13,19 +13,27 @@ module ActionView
|
|
13
13
|
include CaptureHelper
|
14
14
|
include OutputSafetyHelper
|
15
15
|
|
16
|
-
BOOLEAN_ATTRIBUTES = %w(allowfullscreen async autofocus
|
17
|
-
compact controls declare default
|
18
|
-
defaultmuted defaultselected defer
|
19
|
-
enabled formnovalidate hidden indeterminate
|
20
|
-
ismap itemscope loop multiple muted nohref
|
21
|
-
noresize noshade novalidate nowrap open
|
22
|
-
pauseonexit readonly required reversed
|
23
|
-
seamless selected sortable truespeed
|
24
|
-
visible).to_set
|
16
|
+
BOOLEAN_ATTRIBUTES = %w(allowfullscreen allowpaymentrequest async autofocus
|
17
|
+
autoplay checked compact controls declare default
|
18
|
+
defaultchecked defaultmuted defaultselected defer
|
19
|
+
disabled enabled formnovalidate hidden indeterminate
|
20
|
+
inert ismap itemscope loop multiple muted nohref
|
21
|
+
nomodule noresize noshade novalidate nowrap open
|
22
|
+
pauseonexit playsinline readonly required reversed
|
23
|
+
scoped seamless selected sortable truespeed
|
24
|
+
typemustmatch visible).to_set
|
25
25
|
|
26
26
|
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
|
27
|
+
BOOLEAN_ATTRIBUTES.freeze
|
27
28
|
|
28
|
-
|
29
|
+
ARIA_PREFIXES = ["aria", :aria].to_set.freeze
|
30
|
+
DATA_PREFIXES = ["data", :data].to_set.freeze
|
31
|
+
|
32
|
+
TAG_TYPES = {}
|
33
|
+
TAG_TYPES.merge! BOOLEAN_ATTRIBUTES.index_with(:boolean)
|
34
|
+
TAG_TYPES.merge! DATA_PREFIXES.index_with(:data)
|
35
|
+
TAG_TYPES.merge! ARIA_PREFIXES.index_with(:aria)
|
36
|
+
TAG_TYPES.freeze
|
29
37
|
|
30
38
|
PRE_CONTENT_STRINGS = Hash.new { "" }
|
31
39
|
PRE_CONTENT_STRINGS[:textarea] = "\n"
|
@@ -41,6 +49,10 @@ module ActionView
|
|
41
49
|
@view_context = view_context
|
42
50
|
end
|
43
51
|
|
52
|
+
def p(*arguments, **options, &block)
|
53
|
+
tag_string(:p, *arguments, **options, &block)
|
54
|
+
end
|
55
|
+
|
44
56
|
def tag_string(name, content = nil, escape_attributes: true, **options, &block)
|
45
57
|
content = @view_context.capture(self, &block) if block_given?
|
46
58
|
if VOID_ELEMENTS.include?(name) && content.nil?
|
@@ -58,16 +70,34 @@ module ActionView
|
|
58
70
|
|
59
71
|
def tag_options(options, escape = true)
|
60
72
|
return if options.blank?
|
61
|
-
output = ""
|
73
|
+
output = +""
|
62
74
|
sep = " "
|
63
75
|
options.each_pair do |key, value|
|
64
|
-
|
76
|
+
type = TAG_TYPES[key]
|
77
|
+
if type == :data && value.is_a?(Hash)
|
78
|
+
value.each_pair do |k, v|
|
79
|
+
next if v.nil?
|
80
|
+
output << sep
|
81
|
+
output << prefix_tag_option(key, k, v, escape)
|
82
|
+
end
|
83
|
+
elsif type == :aria && value.is_a?(Hash)
|
65
84
|
value.each_pair do |k, v|
|
66
85
|
next if v.nil?
|
86
|
+
|
87
|
+
case v
|
88
|
+
when Array, Hash
|
89
|
+
tokens = TagHelper.build_tag_values(v)
|
90
|
+
next if tokens.none?
|
91
|
+
|
92
|
+
v = safe_join(tokens, " ")
|
93
|
+
else
|
94
|
+
v = v.to_s
|
95
|
+
end
|
96
|
+
|
67
97
|
output << sep
|
68
98
|
output << prefix_tag_option(key, k, v, escape)
|
69
99
|
end
|
70
|
-
elsif
|
100
|
+
elsif type == :boolean
|
71
101
|
if value
|
72
102
|
output << sep
|
73
103
|
output << boolean_tag_option(key)
|
@@ -85,12 +115,15 @@ module ActionView
|
|
85
115
|
end
|
86
116
|
|
87
117
|
def tag_option(key, value, escape)
|
88
|
-
|
89
|
-
|
118
|
+
case value
|
119
|
+
when Array, Hash
|
120
|
+
value = TagHelper.build_tag_values(value) if key.to_s == "class"
|
121
|
+
value = escape ? safe_join(value, " ") : value.join(" ")
|
90
122
|
else
|
91
123
|
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
|
92
124
|
end
|
93
|
-
|
125
|
+
value = value.gsub('"', """) if value.include?('"')
|
126
|
+
%(#{key}="#{value}")
|
94
127
|
end
|
95
128
|
|
96
129
|
private
|
@@ -106,8 +139,8 @@ module ActionView
|
|
106
139
|
true
|
107
140
|
end
|
108
141
|
|
109
|
-
def method_missing(called, *args, &block)
|
110
|
-
tag_string(called, *args, &block)
|
142
|
+
def method_missing(called, *args, **options, &block)
|
143
|
+
tag_string(called, *args, **options, &block)
|
111
144
|
end
|
112
145
|
end
|
113
146
|
|
@@ -151,8 +184,8 @@ module ActionView
|
|
151
184
|
# tag.input type: 'text', disabled: true
|
152
185
|
# # => <input type="text" disabled="disabled">
|
153
186
|
#
|
154
|
-
# HTML5 <tt>data-*</tt> attributes can be set with a
|
155
|
-
# pointing to a hash of sub-attributes.
|
187
|
+
# HTML5 <tt>data-*</tt> and <tt>aria-*</tt> attributes can be set with a
|
188
|
+
# single +data+ or +aria+ key pointing to a hash of sub-attributes.
|
156
189
|
#
|
157
190
|
# To play nicely with JavaScript conventions, sub-attributes are dasherized.
|
158
191
|
#
|
@@ -166,7 +199,7 @@ module ActionView
|
|
166
199
|
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
|
167
200
|
# from 1.4.3.
|
168
201
|
#
|
169
|
-
# tag.div data: { city_state: %w(
|
202
|
+
# tag.div data: { city_state: %w( Chicago IL ) }
|
170
203
|
# # => <div data-city-state="["Chicago","IL"]"></div>
|
171
204
|
#
|
172
205
|
# The generated attributes are escaped by default. This can be disabled using
|
@@ -227,11 +260,14 @@ module ActionView
|
|
227
260
|
# tag("img", src: "open & shut.png")
|
228
261
|
# # => <img src="open & shut.png" />
|
229
262
|
#
|
230
|
-
# tag("img", {src: "open & shut.png"}, false, false)
|
263
|
+
# tag("img", { src: "open & shut.png" }, false, false)
|
231
264
|
# # => <img src="open & shut.png" />
|
232
265
|
#
|
233
|
-
# tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
|
266
|
+
# tag("div", data: { name: 'Stephen', city_state: %w(Chicago IL) })
|
234
267
|
# # => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
|
268
|
+
#
|
269
|
+
# tag("div", class: { highlight: current_user.admin? })
|
270
|
+
# # => <div class="highlight" />
|
235
271
|
def tag(name = nil, options = nil, open = false, escape = true)
|
236
272
|
if name.nil?
|
237
273
|
tag_builder
|
@@ -259,6 +295,8 @@ module ActionView
|
|
259
295
|
# # => <div class="strong"><p>Hello world!</p></div>
|
260
296
|
# content_tag(:div, "Hello world!", class: ["strong", "highlight"])
|
261
297
|
# # => <div class="strong highlight">Hello world!</div>
|
298
|
+
# content_tag(:div, "Hello world!", class: ["strong", { highlight: current_user.admin? }])
|
299
|
+
# # => <div class="strong highlight">Hello world!</div>
|
262
300
|
# content_tag("select", options, multiple: true)
|
263
301
|
# # => <select multiple="multiple">...options...</select>
|
264
302
|
#
|
@@ -275,6 +313,24 @@ module ActionView
|
|
275
313
|
end
|
276
314
|
end
|
277
315
|
|
316
|
+
# Returns a string of tokens built from +args+.
|
317
|
+
#
|
318
|
+
# ==== Examples
|
319
|
+
# token_list("foo", "bar")
|
320
|
+
# # => "foo bar"
|
321
|
+
# token_list("foo", "foo bar")
|
322
|
+
# # => "foo bar"
|
323
|
+
# token_list({ foo: true, bar: false })
|
324
|
+
# # => "foo"
|
325
|
+
# token_list(nil, false, 123, "", "foo", { bar: true })
|
326
|
+
# # => "123 foo bar"
|
327
|
+
def token_list(*args)
|
328
|
+
tokens = build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq
|
329
|
+
|
330
|
+
safe_join(tokens, " ")
|
331
|
+
end
|
332
|
+
alias_method :class_names, :token_list
|
333
|
+
|
278
334
|
# Returns a CDATA section with the given +content+. CDATA sections
|
279
335
|
# are used to escape blocks of text containing characters which would
|
280
336
|
# otherwise be recognized as markup. CDATA sections begin with the string
|
@@ -305,6 +361,26 @@ module ActionView
|
|
305
361
|
end
|
306
362
|
|
307
363
|
private
|
364
|
+
def build_tag_values(*args)
|
365
|
+
tag_values = []
|
366
|
+
|
367
|
+
args.each do |tag_value|
|
368
|
+
case tag_value
|
369
|
+
when Hash
|
370
|
+
tag_value.each do |key, val|
|
371
|
+
tag_values << key.to_s if val && key.present?
|
372
|
+
end
|
373
|
+
when Array
|
374
|
+
tag_values.concat build_tag_values(*tag_value)
|
375
|
+
else
|
376
|
+
tag_values << tag_value.to_s if tag_value.present?
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
tag_values
|
381
|
+
end
|
382
|
+
module_function :build_tag_values
|
383
|
+
|
308
384
|
def tag_builder
|
309
385
|
@tag_builder ||= TagBuilder.new(self)
|
310
386
|
end
|