actionview 5.2.8.1 → 6.0.6.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.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +280 -94
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -3
- data/lib/action_view/base.rb +108 -11
- data/lib/action_view/buffers.rb +15 -0
- data/lib/action_view/cache_expiry.rb +53 -0
- data/lib/action_view/context.rb +5 -9
- data/lib/action_view/digestor.rb +12 -20
- data/lib/action_view/flows.rb +0 -1
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/active_model_helper.rb +0 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +8 -31
- data/lib/action_view/helpers/asset_url_helper.rb +4 -3
- data/lib/action_view/helpers/cache_helper.rb +19 -12
- data/lib/action_view/helpers/capture_helper.rb +4 -0
- data/lib/action_view/helpers/csp_helper.rb +4 -2
- data/lib/action_view/helpers/csrf_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +70 -27
- data/lib/action_view/helpers/form_helper.rb +240 -8
- data/lib/action_view/helpers/form_options_helper.rb +27 -18
- data/lib/action_view/helpers/form_tag_helper.rb +17 -15
- data/lib/action_view/helpers/javascript_helper.rb +9 -8
- data/lib/action_view/helpers/number_helper.rb +8 -2
- data/lib/action_view/helpers/output_safety_helper.rb +1 -1
- data/lib/action_view/helpers/rendering_helper.rb +6 -4
- data/lib/action_view/helpers/sanitize_helper.rb +12 -18
- data/lib/action_view/helpers/tag_helper.rb +8 -7
- data/lib/action_view/helpers/tags/base.rb +9 -6
- data/lib/action_view/helpers/tags/check_box.rb +0 -1
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
- data/lib/action_view/helpers/tags/color_field.rb +1 -2
- data/lib/action_view/helpers/tags/date_field.rb +0 -1
- data/lib/action_view/helpers/tags/date_select.rb +0 -1
- data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
- data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -1
- data/lib/action_view/helpers/tags/label.rb +0 -1
- data/lib/action_view/helpers/tags/month_field.rb +0 -1
- data/lib/action_view/helpers/tags/radio_button.rb +0 -1
- data/lib/action_view/helpers/tags/select.rb +0 -1
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/time_field.rb +0 -1
- data/lib/action_view/helpers/tags/translator.rb +1 -6
- data/lib/action_view/helpers/tags/week_field.rb +0 -1
- data/lib/action_view/helpers/text_helper.rb +3 -4
- data/lib/action_view/helpers/translation_helper.rb +19 -17
- data/lib/action_view/helpers/url_helper.rb +14 -14
- data/lib/action_view/helpers.rb +0 -2
- data/lib/action_view/layouts.rb +5 -8
- data/lib/action_view/log_subscriber.rb +6 -7
- data/lib/action_view/lookup_context.rb +75 -32
- data/lib/action_view/path_set.rb +5 -11
- data/lib/action_view/railtie.rb +24 -1
- data/lib/action_view/record_identifier.rb +2 -3
- data/lib/action_view/renderer/abstract_renderer.rb +56 -4
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +63 -17
- data/lib/action_view/renderer/partial_renderer.rb +67 -57
- data/lib/action_view/renderer/renderer.rb +16 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +5 -7
- data/lib/action_view/renderer/template_renderer.rb +25 -20
- data/lib/action_view/rendering.rb +51 -32
- data/lib/action_view/routing_url_for.rb +12 -11
- data/lib/action_view/template/error.rb +30 -15
- data/lib/action_view/template/handlers/builder.rb +2 -2
- data/lib/action_view/template/handlers/erb/erubi.rb +7 -3
- data/lib/action_view/template/handlers/erb.rb +17 -8
- data/lib/action_view/template/handlers/html.rb +1 -1
- data/lib/action_view/template/handlers/raw.rb +2 -2
- data/lib/action_view/template/handlers.rb +27 -1
- data/lib/action_view/template/html.rb +14 -5
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +28 -0
- data/lib/action_view/template/resolver.rb +134 -135
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +5 -3
- data/lib/action_view/template.rb +102 -71
- data/lib/action_view/test_case.rb +3 -4
- data/lib/action_view/testing/resolvers.rb +33 -21
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/view_paths.rb +25 -2
- data/lib/action_view.rb +4 -2
- data/lib/assets/compiled/rails-ujs.js +30 -4
- metadata +27 -18
- data/lib/action_view/helpers/record_tag_helper.rb +0 -23
@@ -16,7 +16,7 @@ module ActionView
|
|
16
16
|
#
|
17
17
|
# * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
|
18
18
|
#
|
19
|
-
# select("post", "category", Post::CATEGORIES, {include_blank: true})
|
19
|
+
# select("post", "category", Post::CATEGORIES, { include_blank: true })
|
20
20
|
#
|
21
21
|
# could become:
|
22
22
|
#
|
@@ -30,7 +30,7 @@ module ActionView
|
|
30
30
|
#
|
31
31
|
# Example with <tt>@post.person_id => 2</tt>:
|
32
32
|
#
|
33
|
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: 'None'})
|
33
|
+
# select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: 'None' })
|
34
34
|
#
|
35
35
|
# could become:
|
36
36
|
#
|
@@ -43,7 +43,7 @@ module ActionView
|
|
43
43
|
#
|
44
44
|
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
|
45
45
|
#
|
46
|
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'})
|
46
|
+
# select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { prompt: 'Select Person' })
|
47
47
|
#
|
48
48
|
# could become:
|
49
49
|
#
|
@@ -69,7 +69,7 @@ module ActionView
|
|
69
69
|
#
|
70
70
|
# * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
|
71
71
|
#
|
72
|
-
# select("post", "category", Post::CATEGORIES, {disabled: 'restricted'})
|
72
|
+
# select("post", "category", Post::CATEGORIES, { disabled: 'restricted' })
|
73
73
|
#
|
74
74
|
# could become:
|
75
75
|
#
|
@@ -82,7 +82,7 @@ module ActionView
|
|
82
82
|
#
|
83
83
|
# When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
|
84
84
|
#
|
85
|
-
# collection_select(:post, :category_id, Category.all, :id, :name, {disabled: -> (category) { category.archived? }})
|
85
|
+
# collection_select(:post, :category_id, Category.all, :id, :name, { disabled: -> (category) { category.archived? } })
|
86
86
|
#
|
87
87
|
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
|
88
88
|
# <select name="post[category_id]" id="post_category_id">
|
@@ -107,7 +107,7 @@ module ActionView
|
|
107
107
|
#
|
108
108
|
# For example:
|
109
109
|
#
|
110
|
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true })
|
110
|
+
# select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: true })
|
111
111
|
#
|
112
112
|
# would become:
|
113
113
|
#
|
@@ -323,12 +323,12 @@ module ActionView
|
|
323
323
|
#
|
324
324
|
# You can optionally provide HTML attributes as the last element of the array.
|
325
325
|
#
|
326
|
-
# options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
|
326
|
+
# options_for_select([ "Denmark", ["USA", { class: 'bold' }], "Sweden" ], ["USA", "Sweden"])
|
327
327
|
# # => <option value="Denmark">Denmark</option>
|
328
328
|
# # => <option value="USA" class="bold" selected="selected">USA</option>
|
329
329
|
# # => <option value="Sweden" selected="selected">Sweden</option>
|
330
330
|
#
|
331
|
-
# options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
|
331
|
+
# options_for_select([["Dollar", "$", { class: "bold" }], ["Kroner", "DKK", { onclick: "alert('HI');" }]])
|
332
332
|
# # => <option value="$" class="bold">Dollar</option>
|
333
333
|
# # => <option value="DKK" onclick="alert('HI');">Kroner</option>
|
334
334
|
#
|
@@ -463,7 +463,7 @@ module ActionView
|
|
463
463
|
option_tags = options_from_collection_for_select(
|
464
464
|
value_for_collection(group, group_method), option_key_method, option_value_method, selected_key)
|
465
465
|
|
466
|
-
content_tag("optgroup"
|
466
|
+
content_tag("optgroup", option_tags, label: value_for_collection(group, group_label_method))
|
467
467
|
end.join.html_safe
|
468
468
|
end
|
469
469
|
|
@@ -535,7 +535,7 @@ module ActionView
|
|
535
535
|
body = "".html_safe
|
536
536
|
|
537
537
|
if prompt
|
538
|
-
body.safe_concat content_tag("option"
|
538
|
+
body.safe_concat content_tag("option", prompt_text(prompt), value: "")
|
539
539
|
end
|
540
540
|
|
541
541
|
grouped_options.each do |container|
|
@@ -548,7 +548,7 @@ module ActionView
|
|
548
548
|
end
|
549
549
|
|
550
550
|
html_attributes = { label: label }.merge!(html_attributes)
|
551
|
-
body.safe_concat content_tag("optgroup"
|
551
|
+
body.safe_concat content_tag("optgroup", options_for_select(container, selected_key), html_attributes)
|
552
552
|
end
|
553
553
|
|
554
554
|
body
|
@@ -566,9 +566,10 @@ module ActionView
|
|
566
566
|
# an ActiveSupport::TimeZone.
|
567
567
|
#
|
568
568
|
# By default, +model+ is the ActiveSupport::TimeZone constant (which can
|
569
|
-
# be obtained in Active Record as a value object). The
|
570
|
-
#
|
571
|
-
#
|
569
|
+
# be obtained in Active Record as a value object). The +model+ parameter
|
570
|
+
# must respond to +all+ and return an array of objects that represent time
|
571
|
+
# zones; each object must respond to +name+. If a Regexp is given it will
|
572
|
+
# attempt to match the zones using the <code>=~<code> operator.
|
572
573
|
#
|
573
574
|
# NOTE: Only the option tags are returned, you have to wrap this call in
|
574
575
|
# a regular HTML select tag.
|
@@ -584,7 +585,7 @@ module ActionView
|
|
584
585
|
end
|
585
586
|
|
586
587
|
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
|
587
|
-
zone_options.safe_concat content_tag("option"
|
588
|
+
zone_options.safe_concat content_tag("option", "-------------", value: "", disabled: true)
|
588
589
|
zone_options.safe_concat "\n"
|
589
590
|
|
590
591
|
zones = zones - priority_zones
|
@@ -654,7 +655,7 @@ module ActionView
|
|
654
655
|
#
|
655
656
|
# ==== Gotcha
|
656
657
|
#
|
657
|
-
# The HTML specification says when nothing is
|
658
|
+
# The HTML specification says when nothing is selected on a collection of radio buttons
|
658
659
|
# web browsers do not send any value to server.
|
659
660
|
# Unfortunately this introduces a gotcha:
|
660
661
|
# if a +User+ model has a +category_id+ field and in the form no category is selected, no +category_id+ parameter is sent. So,
|
@@ -794,7 +795,7 @@ module ActionView
|
|
794
795
|
def extract_values_from_collection(collection, value_method, selected)
|
795
796
|
if selected.is_a?(Proc)
|
796
797
|
collection.map do |element|
|
797
|
-
element
|
798
|
+
public_or_deprecated_send(element, value_method) if selected.call(element)
|
798
799
|
end.compact
|
799
800
|
else
|
800
801
|
selected
|
@@ -802,7 +803,15 @@ module ActionView
|
|
802
803
|
end
|
803
804
|
|
804
805
|
def value_for_collection(item, value)
|
805
|
-
value.respond_to?(:call) ? value.call(item) : item
|
806
|
+
value.respond_to?(:call) ? value.call(item) : public_or_deprecated_send(item, value)
|
807
|
+
end
|
808
|
+
|
809
|
+
def public_or_deprecated_send(item, value)
|
810
|
+
item.public_send(value)
|
811
|
+
rescue NoMethodError
|
812
|
+
raise unless item.respond_to?(value, true) && !item.respond_to?(value)
|
813
|
+
ActiveSupport::Deprecation.warn "Using private methods from view helpers is deprecated (calling private #{item.class}##{value})"
|
814
|
+
item.send(value)
|
806
815
|
end
|
807
816
|
|
808
817
|
def prompt_text(prompt)
|
@@ -22,7 +22,9 @@ module ActionView
|
|
22
22
|
mattr_accessor :embed_authenticity_token_in_remote_forms
|
23
23
|
self.embed_authenticity_token_in_remote_forms = nil
|
24
24
|
|
25
|
-
|
25
|
+
mattr_accessor :default_enforce_utf8, default: true
|
26
|
+
|
27
|
+
# Starts a form tag that points the action to a URL configured with <tt>url_for_options</tt> just like
|
26
28
|
# ActionController::Base#url_for. The method for the form defaults to POST.
|
27
29
|
#
|
28
30
|
# ==== Options
|
@@ -135,7 +137,8 @@ module ActionView
|
|
135
137
|
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
|
136
138
|
|
137
139
|
if options.include?(:include_blank)
|
138
|
-
include_blank = options
|
140
|
+
include_blank = options[:include_blank]
|
141
|
+
options = options.except(:include_blank)
|
139
142
|
options_for_blank_options_tag = { value: "" }
|
140
143
|
|
141
144
|
if include_blank == true
|
@@ -144,15 +147,15 @@ module ActionView
|
|
144
147
|
end
|
145
148
|
|
146
149
|
if include_blank
|
147
|
-
option_tags = content_tag("option"
|
150
|
+
option_tags = content_tag("option", include_blank, options_for_blank_options_tag).safe_concat(option_tags)
|
148
151
|
end
|
149
152
|
end
|
150
153
|
|
151
154
|
if prompt = options.delete(:prompt)
|
152
|
-
option_tags = content_tag("option"
|
155
|
+
option_tags = content_tag("option", prompt, value: "").safe_concat(option_tags)
|
153
156
|
end
|
154
157
|
|
155
|
-
content_tag "select"
|
158
|
+
content_tag "select", option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
|
156
159
|
end
|
157
160
|
|
158
161
|
# Creates a standard text field; use these text fields to input smaller chunks of text like a username
|
@@ -389,8 +392,8 @@ module ActionView
|
|
389
392
|
# * Any other key creates standard HTML options for the tag.
|
390
393
|
#
|
391
394
|
# ==== Examples
|
392
|
-
# radio_button_tag '
|
393
|
-
# # => <input id="
|
395
|
+
# radio_button_tag 'favorite_color', 'maroon'
|
396
|
+
# # => <input id="favorite_color_maroon" name="favorite_color" type="radio" value="maroon" />
|
394
397
|
#
|
395
398
|
# radio_button_tag 'receive_updates', 'no', true
|
396
399
|
# # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
|
@@ -577,7 +580,7 @@ module ActionView
|
|
577
580
|
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
|
578
581
|
def field_set_tag(legend = nil, options = nil, &block)
|
579
582
|
output = tag(:fieldset, options, true)
|
580
|
-
output.safe_concat(content_tag("legend"
|
583
|
+
output.safe_concat(content_tag("legend", legend)) unless legend.blank?
|
581
584
|
output.concat(capture(&block)) if block_given?
|
582
585
|
output.safe_concat("</fieldset>")
|
583
586
|
end
|
@@ -869,7 +872,7 @@ module ActionView
|
|
869
872
|
})
|
870
873
|
end
|
871
874
|
|
872
|
-
if html_options.delete("enforce_utf8") {
|
875
|
+
if html_options.delete("enforce_utf8") { default_enforce_utf8 }
|
873
876
|
utf8_enforcer_tag + method_tag
|
874
877
|
else
|
875
878
|
method_tag
|
@@ -893,16 +896,15 @@ module ActionView
|
|
893
896
|
end
|
894
897
|
|
895
898
|
def set_default_disable_with(value, tag_options)
|
896
|
-
|
897
|
-
data = tag_options["data"]
|
899
|
+
data = tag_options.fetch("data", {})
|
898
900
|
|
899
|
-
|
901
|
+
if tag_options["data-disable-with"] == false || data["disable_with"] == false
|
902
|
+
data.delete("disable_with")
|
903
|
+
elsif ActionView::Base.automatically_disable_submit_tag
|
900
904
|
disable_with_text = tag_options["data-disable-with"]
|
901
|
-
disable_with_text ||= data["disable_with"]
|
905
|
+
disable_with_text ||= data["disable_with"]
|
902
906
|
disable_with_text ||= value.to_s.clone
|
903
907
|
tag_options.deep_merge!("data" => { "disable_with" => disable_with_text })
|
904
|
-
else
|
905
|
-
data.delete("disable_with") if data
|
906
908
|
end
|
907
909
|
|
908
910
|
tag_options.delete("data-disable-with")
|
@@ -17,8 +17,8 @@ module ActionView
|
|
17
17
|
"$" => "\\$"
|
18
18
|
}
|
19
19
|
|
20
|
-
JS_ESCAPE_MAP["\342\200\250".
|
21
|
-
JS_ESCAPE_MAP["\342\200\251".
|
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!] = "
"
|
22
22
|
|
23
23
|
# Escapes carriage returns and single and double quotes for JavaScript segments.
|
24
24
|
#
|
@@ -27,12 +27,13 @@ module ActionView
|
|
27
27
|
#
|
28
28
|
# $('some_element').replaceWith('<%= j render 'some/element_template' %>');
|
29
29
|
def escape_javascript(javascript)
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
javascript = javascript.to_s
|
31
|
+
if javascript.empty?
|
32
|
+
result = ""
|
33
33
|
else
|
34
|
-
"
|
34
|
+
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
|
35
35
|
end
|
36
|
+
javascript.html_safe? ? result.html_safe : result
|
36
37
|
end
|
37
38
|
|
38
39
|
alias_method :j, :escape_javascript
|
@@ -67,7 +68,7 @@ module ActionView
|
|
67
68
|
# <% end -%>
|
68
69
|
#
|
69
70
|
# If you have a content security policy enabled then you can add an automatic
|
70
|
-
# nonce value by passing
|
71
|
+
# nonce value by passing <tt>nonce: true</tt> as part of +html_options+. Example:
|
71
72
|
#
|
72
73
|
# <%= javascript_tag nonce: true do -%>
|
73
74
|
# alert('All is good')
|
@@ -85,7 +86,7 @@ module ActionView
|
|
85
86
|
html_options[:nonce] = content_security_policy_nonce
|
86
87
|
end
|
87
88
|
|
88
|
-
content_tag("script"
|
89
|
+
content_tag("script", javascript_cdata_section(content), html_options)
|
89
90
|
end
|
90
91
|
|
91
92
|
def javascript_cdata_section(content) #:nodoc:
|
@@ -57,7 +57,7 @@ module ActionView
|
|
57
57
|
#
|
58
58
|
# number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
|
59
59
|
# # => "(755) 6123-4567"
|
60
|
-
# number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
|
60
|
+
# number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
|
61
61
|
# # => "133-1234-5678"
|
62
62
|
def number_to_phone(number, options = {})
|
63
63
|
return unless number
|
@@ -100,6 +100,9 @@ module ActionView
|
|
100
100
|
# absolute value of the number.
|
101
101
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
102
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+).
|
103
106
|
#
|
104
107
|
# ==== Examples
|
105
108
|
#
|
@@ -111,12 +114,16 @@ module ActionView
|
|
111
114
|
#
|
112
115
|
# number_to_currency("123a456", raise: true) # => InvalidNumberError
|
113
116
|
#
|
117
|
+
# number_to_currency(-0.456789, precision: 0)
|
118
|
+
# # => "$0"
|
114
119
|
# number_to_currency(-1234567890.50, negative_format: "(%u%n)")
|
115
120
|
# # => ($1,234,567,890.50)
|
116
121
|
# number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
|
117
122
|
# # => R$1234567890,50
|
118
123
|
# number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
|
119
124
|
# # => 1234567890,50 R$
|
125
|
+
# number_to_currency(1234567890.50, strip_insignificant_zeros: true)
|
126
|
+
# # => "$1,234,567,890.5"
|
120
127
|
def number_to_currency(number, options = {})
|
121
128
|
delegate_number_helper_method(:number_to_currency, number, options)
|
122
129
|
end
|
@@ -398,7 +405,6 @@ module ActionView
|
|
398
405
|
end
|
399
406
|
|
400
407
|
private
|
401
|
-
|
402
408
|
def delegate_number_helper_method(method, number, options)
|
403
409
|
return unless number
|
404
410
|
options = escape_unsafe_options(options.symbolize_keys)
|
@@ -38,7 +38,7 @@ module ActionView #:nodoc:
|
|
38
38
|
|
39
39
|
# Converts the array to a comma-separated sentence where the last element is
|
40
40
|
# joined by the connector word. This is the html_safe-aware version of
|
41
|
-
# ActiveSupport's {Array#to_sentence}[
|
41
|
+
# ActiveSupport's {Array#to_sentence}[https://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
|
42
42
|
#
|
43
43
|
def to_sentence(array, options = {})
|
44
44
|
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
|
@@ -27,10 +27,12 @@ module ActionView
|
|
27
27
|
def render(options = {}, locals = {}, &block)
|
28
28
|
case options
|
29
29
|
when Hash
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
in_rendering_context(options) do |renderer|
|
31
|
+
if block_given?
|
32
|
+
view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
|
33
|
+
else
|
34
|
+
view_renderer.render(self, options)
|
35
|
+
end
|
34
36
|
end
|
35
37
|
else
|
36
38
|
view_renderer.render_partial(self, partial: options, locals: locals, &block)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/object/try"
|
4
3
|
require "rails-html-sanitizer"
|
5
4
|
|
6
5
|
module ActionView
|
@@ -10,14 +9,14 @@ module ActionView
|
|
10
9
|
# These helper methods extend Action View making them callable within your template files.
|
11
10
|
module SanitizeHelper
|
12
11
|
extend ActiveSupport::Concern
|
13
|
-
# Sanitizes HTML input, stripping all tags and attributes
|
12
|
+
# Sanitizes HTML input, stripping all but known-safe tags and attributes.
|
14
13
|
#
|
15
14
|
# It also strips href/src attributes with unsafe protocols like
|
16
15
|
# <tt>javascript:</tt>, while also protecting against attempts to use Unicode,
|
17
16
|
# ASCII, and hex character references to work around these protocol filters.
|
18
17
|
# All special characters will be escaped.
|
19
18
|
#
|
20
|
-
# The default sanitizer is Rails::Html::
|
19
|
+
# The default sanitizer is Rails::Html::SafeListSanitizer. See {Rails HTML
|
21
20
|
# Sanitizers}[https://github.com/rails/rails-html-sanitizer] for more information.
|
22
21
|
#
|
23
22
|
# Custom sanitization rules can also be provided.
|
@@ -40,7 +39,7 @@ module ActionView
|
|
40
39
|
#
|
41
40
|
# <%= sanitize @comment.body %>
|
42
41
|
#
|
43
|
-
# Providing custom
|
42
|
+
# Providing custom lists of permitted tags and attributes:
|
44
43
|
#
|
45
44
|
# <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %>
|
46
45
|
#
|
@@ -80,12 +79,12 @@ module ActionView
|
|
80
79
|
# config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a']
|
81
80
|
# config.action_view.sanitized_allowed_attributes = ['href', 'title']
|
82
81
|
def sanitize(html, options = {})
|
83
|
-
self.class.
|
82
|
+
self.class.safe_list_sanitizer.sanitize(html, options)&.html_safe
|
84
83
|
end
|
85
84
|
|
86
85
|
# Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
|
87
86
|
def sanitize_css(style)
|
88
|
-
self.class.
|
87
|
+
self.class.safe_list_sanitizer.sanitize_css(style)
|
89
88
|
end
|
90
89
|
|
91
90
|
# Strips all HTML tags from +html+, including comments and special characters.
|
@@ -123,20 +122,18 @@ module ActionView
|
|
123
122
|
end
|
124
123
|
|
125
124
|
module ClassMethods #:nodoc:
|
126
|
-
attr_writer :full_sanitizer, :link_sanitizer, :
|
125
|
+
attr_writer :full_sanitizer, :link_sanitizer, :safe_list_sanitizer
|
127
126
|
|
128
|
-
# Vendors the full, link and white list sanitizers.
|
129
|
-
# Provided strictly for compatibility and can be removed in Rails 5.1.
|
130
127
|
def sanitizer_vendor
|
131
128
|
Rails::Html::Sanitizer
|
132
129
|
end
|
133
130
|
|
134
131
|
def sanitized_allowed_tags
|
135
|
-
sanitizer_vendor.
|
132
|
+
sanitizer_vendor.safe_list_sanitizer.allowed_tags
|
136
133
|
end
|
137
134
|
|
138
135
|
def sanitized_allowed_attributes
|
139
|
-
sanitizer_vendor.
|
136
|
+
sanitizer_vendor.safe_list_sanitizer.allowed_attributes
|
140
137
|
end
|
141
138
|
|
142
139
|
# Gets the Rails::Html::FullSanitizer instance used by +strip_tags+. Replace with
|
@@ -145,7 +142,6 @@ module ActionView
|
|
145
142
|
# class Application < Rails::Application
|
146
143
|
# config.action_view.full_sanitizer = MySpecialSanitizer.new
|
147
144
|
# end
|
148
|
-
#
|
149
145
|
def full_sanitizer
|
150
146
|
@full_sanitizer ||= sanitizer_vendor.full_sanitizer.new
|
151
147
|
end
|
@@ -156,20 +152,18 @@ module ActionView
|
|
156
152
|
# class Application < Rails::Application
|
157
153
|
# config.action_view.link_sanitizer = MySpecialSanitizer.new
|
158
154
|
# end
|
159
|
-
#
|
160
155
|
def link_sanitizer
|
161
156
|
@link_sanitizer ||= sanitizer_vendor.link_sanitizer.new
|
162
157
|
end
|
163
158
|
|
164
|
-
# Gets the Rails::Html::
|
159
|
+
# Gets the Rails::Html::SafeListSanitizer instance used by sanitize and +sanitize_css+.
|
165
160
|
# Replace with any object that responds to +sanitize+.
|
166
161
|
#
|
167
162
|
# class Application < Rails::Application
|
168
|
-
# config.action_view.
|
163
|
+
# config.action_view.safe_list_sanitizer = MySpecialSanitizer.new
|
169
164
|
# end
|
170
|
-
|
171
|
-
|
172
|
-
@white_list_sanitizer ||= sanitizer_vendor.white_list_sanitizer.new
|
165
|
+
def safe_list_sanitizer
|
166
|
+
@safe_list_sanitizer ||= sanitizer_vendor.safe_list_sanitizer.new
|
173
167
|
end
|
174
168
|
end
|
175
169
|
end
|
@@ -65,7 +65,7 @@ module ActionView
|
|
65
65
|
|
66
66
|
def tag_options(options, escape = true)
|
67
67
|
return if options.blank?
|
68
|
-
output = ""
|
68
|
+
output = +""
|
69
69
|
sep = " "
|
70
70
|
options.each_pair do |key, value|
|
71
71
|
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
|
@@ -95,11 +95,12 @@ module ActionView
|
|
95
95
|
key = ERB::Util.xml_name_escape(key) if escape
|
96
96
|
|
97
97
|
if value.is_a?(Array)
|
98
|
-
value = escape ? safe_join(value, " "
|
98
|
+
value = escape ? safe_join(value, " ") : value.join(" ")
|
99
99
|
else
|
100
|
-
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
|
100
|
+
value = escape ? ERB::Util.unwrapped_html_escape(value).dup : value.to_s.dup
|
101
101
|
end
|
102
|
-
|
102
|
+
value.gsub!('"', """)
|
103
|
+
%(#{key}="#{value}")
|
103
104
|
end
|
104
105
|
|
105
106
|
private
|
@@ -122,7 +123,7 @@ module ActionView
|
|
122
123
|
escape_attributes_option_provided = options.has_key?(:escape_attributes)
|
123
124
|
|
124
125
|
if escape_attributes_option_provided
|
125
|
-
ActiveSupport::Deprecation.warn(
|
126
|
+
ActiveSupport::Deprecation.warn(<<~MSG)
|
126
127
|
Use of the option :escape_attributes is deprecated. It currently \
|
127
128
|
escapes both names and values of tags and attributes and it is \
|
128
129
|
equivalent to :escape. If any of them are enabled, the escaping \
|
@@ -257,10 +258,10 @@ module ActionView
|
|
257
258
|
# tag("img", src: "open & shut.png")
|
258
259
|
# # => <img src="open & shut.png" />
|
259
260
|
#
|
260
|
-
# tag("img", {src: "open & shut.png"}, false, false)
|
261
|
+
# tag("img", { src: "open & shut.png" }, false, false)
|
261
262
|
# # => <img src="open & shut.png" />
|
262
263
|
#
|
263
|
-
# tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
|
264
|
+
# tag("div", data: { name: 'Stephen', city_state: %w(Chicago IL) })
|
264
265
|
# # => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
|
265
266
|
def tag(name = nil, options = nil, open = false, escape = true)
|
266
267
|
if name.nil?
|
@@ -34,7 +34,6 @@ module ActionView
|
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
37
|
-
|
38
37
|
def value
|
39
38
|
if @allow_method_names_outside_object
|
40
39
|
object.public_send @method_name if object && object.respond_to?(@method_name)
|
@@ -109,11 +108,11 @@ module ActionView
|
|
109
108
|
# a little duplication to construct less strings
|
110
109
|
case
|
111
110
|
when @object_name.empty?
|
112
|
-
"#{sanitized_method_name}#{"[]"
|
111
|
+
"#{sanitized_method_name}#{multiple ? "[]" : ""}"
|
113
112
|
when index
|
114
|
-
"#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]"
|
113
|
+
"#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
|
115
114
|
else
|
116
|
-
"#{@object_name}[#{sanitized_method_name}]#{"[]"
|
115
|
+
"#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
|
117
116
|
end
|
118
117
|
end
|
119
118
|
|
@@ -138,7 +137,7 @@ module ActionView
|
|
138
137
|
end
|
139
138
|
|
140
139
|
def sanitized_value(value)
|
141
|
-
value.to_s.gsub(
|
140
|
+
value.to_s.gsub(/[\s\.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
|
142
141
|
end
|
143
142
|
|
144
143
|
def select_content_tag(option_tags, options, html_options)
|
@@ -170,7 +169,11 @@ module ActionView
|
|
170
169
|
option_tags = tag_builder.content_tag_string("option", options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, value: "") + "\n" + option_tags
|
171
170
|
end
|
172
171
|
if value.blank? && options[:prompt]
|
173
|
-
|
172
|
+
tag_options = { value: "" }.tap do |prompt_opts|
|
173
|
+
prompt_opts[:disabled] = true if options[:disabled] == ""
|
174
|
+
prompt_opts[:selected] = true if options[:selected] == ""
|
175
|
+
end
|
176
|
+
option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
|
174
177
|
end
|
175
178
|
option_tags
|
176
179
|
end
|