actionview 7.0.8.7 → 7.1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +309 -321
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- 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 +30 -10
- 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 +2 -2
- data/lib/action_view/helpers/date_helper.rb +17 -19
- data/lib/action_view/helpers/debug_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +54 -25
- data/lib/action_view/helpers/form_options_helper.rb +2 -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 -330
- data/lib/action_view/helpers/output_safety_helper.rb +2 -2
- 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 +3 -0
- 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 +46 -17
- 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 +2 -1
- 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 +5 -5
- 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 +10 -2
- 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 +22 -23
- data/lib/assets/compiled/rails-ujs.js +0 -777
@@ -8,8 +8,9 @@ require "action_view/helpers/tag_helper"
|
|
8
8
|
require "action_view/helpers/output_safety_helper"
|
9
9
|
|
10
10
|
module ActionView
|
11
|
-
# = Action View Text Helpers
|
12
11
|
module Helpers # :nodoc:
|
12
|
+
# = Action View Text \Helpers
|
13
|
+
#
|
13
14
|
# The TextHelper module provides a set of methods for filtering, formatting
|
14
15
|
# and transforming strings, which can reduce the amount of inline Ruby code in
|
15
16
|
# your views. These helper methods extend Action View making them callable
|
@@ -40,21 +41,25 @@ module ActionView
|
|
40
41
|
include OutputSafetyHelper
|
41
42
|
|
42
43
|
# The preferred method of outputting text in your views is to use the
|
43
|
-
#
|
44
|
+
# <tt><%= "text" %></tt> eRuby syntax. The regular +puts+ and +print+ methods
|
44
45
|
# do not operate as expected in an eRuby code block. If you absolutely must
|
45
|
-
# output text within a non-output code block (i.e.,
|
46
|
+
# output text within a non-output code block (i.e., <tt><% %></tt>), you
|
47
|
+
# can use the +concat+ method.
|
48
|
+
#
|
49
|
+
# <% concat "hello" %> is equivalent to <%= "hello" %>
|
46
50
|
#
|
47
51
|
# <%
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
# if logged_in
|
52
|
-
# concat "Logged in!"
|
53
|
-
# else
|
54
|
-
# concat link_to('login', action: :login)
|
55
|
-
# end
|
56
|
-
# # will either display "Logged in!" or a login link
|
52
|
+
# unless signed_in?
|
53
|
+
# concat link_to("Sign In", action: :sign_in)
|
54
|
+
# end
|
57
55
|
# %>
|
56
|
+
#
|
57
|
+
# is equivalent to
|
58
|
+
#
|
59
|
+
# <% unless signed_in? %>
|
60
|
+
# <%= link_to "Sign In", action: :sign_in %>
|
61
|
+
# <% end %>
|
62
|
+
#
|
58
63
|
def concat(string)
|
59
64
|
output_buffer << string
|
60
65
|
end
|
@@ -63,17 +68,36 @@ module ActionView
|
|
63
68
|
output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string)
|
64
69
|
end
|
65
70
|
|
66
|
-
# Truncates
|
67
|
-
#
|
68
|
-
#
|
71
|
+
# Truncates +text+ if it is longer than a specified +:length+. If +text+
|
72
|
+
# is truncated, an omission marker will be appended to the result for a
|
73
|
+
# total length not exceeding +:length+.
|
74
|
+
#
|
75
|
+
# You can also pass a block to render and append extra content after the
|
76
|
+
# omission marker when +text+ is truncated. However, this content _can_
|
77
|
+
# cause the total length to exceed +:length+ characters.
|
78
|
+
#
|
79
|
+
# The result will be escaped unless <tt>escape: false</tt> is specified.
|
80
|
+
# In any case, the result will be marked HTML-safe. Care should be taken
|
81
|
+
# if +text+ might contain HTML tags or entities, because truncation could
|
82
|
+
# produce invalid HTML, such as unbalanced or incomplete tags.
|
69
83
|
#
|
70
|
-
#
|
84
|
+
# ==== Options
|
85
|
+
#
|
86
|
+
# [+:length+]
|
87
|
+
# The maximum number of characters that should be returned, excluding
|
88
|
+
# any extra content from the block. Defaults to 30.
|
89
|
+
#
|
90
|
+
# [+:omission+]
|
91
|
+
# The string to append after truncating. Defaults to <tt>"..."</tt>.
|
92
|
+
#
|
93
|
+
# [+:separator+]
|
94
|
+
# A string or regexp used to find a breaking point at which to truncate.
|
95
|
+
# By default, truncation can occur at any character in +text+.
|
71
96
|
#
|
72
|
-
#
|
97
|
+
# [+:escape+]
|
98
|
+
# Whether to escape the result. Defaults to true.
|
73
99
|
#
|
74
|
-
#
|
75
|
-
# +false+. Care should be taken if +text+ contains HTML tags or entities, because truncation
|
76
|
-
# may produce invalid HTML (such as unbalanced or incomplete tags).
|
100
|
+
# ==== Examples
|
77
101
|
#
|
78
102
|
# truncate("Once upon a time in a world far far away")
|
79
103
|
# # => "Once upon a time in a world..."
|
@@ -94,7 +118,7 @@ module ActionView
|
|
94
118
|
# # => "<p>Once upon a time in a wo..."
|
95
119
|
#
|
96
120
|
# truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
|
97
|
-
# # => "Once upon a time in a
|
121
|
+
# # => "Once upon a time in a world...<a href=\"#\">Continue</a>"
|
98
122
|
def truncate(text, options = {}, &block)
|
99
123
|
if text
|
100
124
|
length = options.fetch(:length, 30)
|
@@ -106,76 +130,108 @@ module ActionView
|
|
106
130
|
end
|
107
131
|
end
|
108
132
|
|
109
|
-
# Highlights
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
133
|
+
# Highlights occurrences of +phrases+ in +text+ by formatting them with a
|
134
|
+
# highlighter string. +phrases+ can be one or more strings or regular
|
135
|
+
# expressions. The result will be marked HTML safe. By default, +text+ is
|
136
|
+
# sanitized before highlighting to prevent possible XSS attacks.
|
137
|
+
#
|
138
|
+
# If a block is specified, it will be used instead of the highlighter
|
139
|
+
# string. Each occurrence of a phrase will be passed to the block, and its
|
140
|
+
# return value will be inserted into the final result.
|
141
|
+
#
|
142
|
+
# ==== Options
|
143
|
+
#
|
144
|
+
# [+:highlighter+]
|
145
|
+
# The highlighter string. Uses <tt>\1</tt> as the placeholder for a
|
146
|
+
# phrase, similar to +String#sub+. Defaults to <tt>"<mark>\1</mark>"</tt>.
|
147
|
+
# This option is ignored if a block is specified.
|
148
|
+
#
|
149
|
+
# [+:sanitize+]
|
150
|
+
# Whether to sanitize +text+ before highlighting. Defaults to true.
|
151
|
+
#
|
152
|
+
# ==== Examples
|
115
153
|
#
|
116
154
|
# highlight('You searched for: rails', 'rails')
|
117
|
-
# # => You searched for: <mark>rails</mark>
|
155
|
+
# # => "You searched for: <mark>rails</mark>"
|
118
156
|
#
|
119
157
|
# highlight('You searched for: rails', /for|rails/)
|
120
|
-
# # => You searched <mark>for</mark>: <mark>rails</mark>
|
158
|
+
# # => "You searched <mark>for</mark>: <mark>rails</mark>"
|
121
159
|
#
|
122
160
|
# highlight('You searched for: ruby, rails, dhh', 'actionpack')
|
123
|
-
# # => You searched for: ruby, rails, dhh
|
161
|
+
# # => "You searched for: ruby, rails, dhh"
|
124
162
|
#
|
125
163
|
# highlight('You searched for: rails', ['for', 'rails'], highlighter: '<em>\1</em>')
|
126
|
-
# # => You searched <em>for</em>: <em>rails</em>
|
164
|
+
# # => "You searched <em>for</em>: <em>rails</em>"
|
127
165
|
#
|
128
166
|
# highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
|
129
|
-
# # => You searched for: <a href
|
167
|
+
# # => "You searched for: <a href=\"search?q=rails\">rails</a>"
|
130
168
|
#
|
131
169
|
# highlight('You searched for: rails', 'rails') { |match| link_to(search_path(q: match, match)) }
|
132
|
-
# # => You searched for: <a href
|
170
|
+
# # => "You searched for: <a href=\"search?q=rails\">rails</a>"
|
133
171
|
#
|
134
172
|
# highlight('<a href="javascript:alert(\'no!\')">ruby</a> on rails', 'rails', sanitize: false)
|
135
|
-
# # => <a href
|
173
|
+
# # => "<a href=\"javascript:alert('no!')\">ruby</a> on <mark>rails</mark>"
|
136
174
|
def highlight(text, phrases, options = {}, &block)
|
137
175
|
text = sanitize(text) if options.fetch(:sanitize, true)
|
138
176
|
|
139
177
|
if text.blank? || phrases.blank?
|
140
178
|
text || ""
|
141
179
|
else
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
180
|
+
patterns = Array(phrases).map { |phrase| Regexp === phrase ? phrase : Regexp.escape(phrase) }
|
181
|
+
pattern = /(#{patterns.join("|")})/i
|
182
|
+
highlighter = options.fetch(:highlighter, '<mark>\1</mark>') unless block
|
183
|
+
|
184
|
+
text.scan(/<[^>]*|[^<]+/).each do |segment|
|
185
|
+
if !segment.start_with?("<")
|
186
|
+
if block
|
187
|
+
segment.gsub!(pattern, &block)
|
188
|
+
else
|
189
|
+
segment.gsub!(pattern, highlighter)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end.join
|
152
193
|
end.html_safe
|
153
194
|
end
|
154
195
|
|
155
|
-
# Extracts
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
196
|
+
# Extracts the first occurrence of +phrase+ plus surrounding text from
|
197
|
+
# +text+. An omission marker is prepended / appended if the start / end of
|
198
|
+
# the result does not coincide with the start / end of +text+. The result
|
199
|
+
# is always stripped in any case. Returns +nil+ if +phrase+ isn't found.
|
200
|
+
#
|
201
|
+
# ==== Options
|
202
|
+
#
|
203
|
+
# [+:radius+]
|
204
|
+
# The number of characters (or tokens — see +:separator+ option) around
|
205
|
+
# +phrase+ to include in the result. Defaults to 100.
|
206
|
+
#
|
207
|
+
# [+:omission+]
|
208
|
+
# The marker to prepend / append when the start / end of the excerpt
|
209
|
+
# does not coincide with the start / end of +text+. Defaults to
|
210
|
+
# <tt>"..."</tt>.
|
211
|
+
#
|
212
|
+
# [+:separator+]
|
213
|
+
# The separator between tokens to count for +:radius+. Defaults to
|
214
|
+
# <tt>""</tt>, which treats each character as a token.
|
215
|
+
#
|
216
|
+
# ==== Examples
|
161
217
|
#
|
162
218
|
# excerpt('This is an example', 'an', radius: 5)
|
163
|
-
# # => ...s is an exam...
|
219
|
+
# # => "...s is an exam..."
|
164
220
|
#
|
165
221
|
# excerpt('This is an example', 'is', radius: 5)
|
166
|
-
# # => This is a...
|
222
|
+
# # => "This is a..."
|
167
223
|
#
|
168
224
|
# excerpt('This is an example', 'is')
|
169
|
-
# # => This is an example
|
225
|
+
# # => "This is an example"
|
170
226
|
#
|
171
227
|
# excerpt('This next thing is an example', 'ex', radius: 2)
|
172
|
-
# # => ...next...
|
228
|
+
# # => "...next..."
|
173
229
|
#
|
174
230
|
# excerpt('This is also an example', 'an', radius: 8, omission: '<chop> ')
|
175
|
-
# # => <chop> is also an example
|
231
|
+
# # => "<chop> is also an example"
|
176
232
|
#
|
177
233
|
# excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
|
178
|
-
# # => ...a very beautiful...
|
234
|
+
# # => "...a very beautiful..."
|
179
235
|
def excerpt(text, phrase, options = {})
|
180
236
|
return unless text && phrase
|
181
237
|
|
@@ -211,26 +267,26 @@ module ActionView
|
|
211
267
|
# Attempts to pluralize the +singular+ word unless +count+ is 1. If
|
212
268
|
# +plural+ is supplied, it will use that when count is > 1, otherwise
|
213
269
|
# it will use the Inflector to determine the plural form for the given locale,
|
214
|
-
# which defaults to I18n.locale
|
270
|
+
# which defaults to +I18n.locale+.
|
215
271
|
#
|
216
272
|
# The word will be pluralized using rules defined for the locale
|
217
273
|
# (you must define your own inflection rules for languages other than English).
|
218
274
|
# See ActiveSupport::Inflector.pluralize
|
219
275
|
#
|
220
276
|
# pluralize(1, 'person')
|
221
|
-
# # => 1 person
|
277
|
+
# # => "1 person"
|
222
278
|
#
|
223
279
|
# pluralize(2, 'person')
|
224
|
-
# # => 2 people
|
280
|
+
# # => "2 people"
|
225
281
|
#
|
226
282
|
# pluralize(3, 'person', plural: 'users')
|
227
|
-
# # => 3 users
|
283
|
+
# # => "3 users"
|
228
284
|
#
|
229
285
|
# pluralize(0, 'person')
|
230
|
-
# # => 0 people
|
286
|
+
# # => "0 people"
|
231
287
|
#
|
232
288
|
# pluralize(2, 'Person', locale: :de)
|
233
|
-
# # => 2 Personen
|
289
|
+
# # => "2 Personen"
|
234
290
|
def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
|
235
291
|
word = if count == 1 || count.to_s.match?(/^1(\.0+)?$/)
|
236
292
|
singular
|
@@ -246,25 +302,35 @@ module ActionView
|
|
246
302
|
# (which is 80 by default).
|
247
303
|
#
|
248
304
|
# word_wrap('Once upon a time')
|
249
|
-
# # => Once upon a time
|
305
|
+
# # => "Once upon a time"
|
250
306
|
#
|
251
307
|
# word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
|
252
|
-
# # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\na successor to the throne turned out to be more trouble than anyone could have\nimagined...
|
308
|
+
# # => "Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\na successor to the throne turned out to be more trouble than anyone could have\nimagined..."
|
253
309
|
#
|
254
310
|
# word_wrap('Once upon a time', line_width: 8)
|
255
|
-
# # => Once\nupon a\ntime
|
311
|
+
# # => "Once\nupon a\ntime"
|
256
312
|
#
|
257
313
|
# word_wrap('Once upon a time', line_width: 1)
|
258
|
-
# # => Once\nupon\na\ntime
|
314
|
+
# # => "Once\nupon\na\ntime"
|
259
315
|
#
|
260
|
-
#
|
316
|
+
# You can also specify a custom +break_sequence+ ("\n" by default):
|
261
317
|
#
|
262
318
|
# word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n")
|
263
|
-
# # => Once\r\nupon\r\na\r\ntime
|
319
|
+
# # => "Once\r\nupon\r\na\r\ntime"
|
264
320
|
def word_wrap(text, line_width: 80, break_sequence: "\n")
|
265
|
-
|
266
|
-
|
267
|
-
|
321
|
+
return +"" if text.empty?
|
322
|
+
|
323
|
+
# Match up to `line_width` characters, followed by one of
|
324
|
+
# (1) non-newline whitespace plus an optional newline
|
325
|
+
# (2) the end of the string, ignoring any trailing newlines
|
326
|
+
# (3) a newline
|
327
|
+
#
|
328
|
+
# -OR-
|
329
|
+
#
|
330
|
+
# Match an empty line
|
331
|
+
pattern = /(.{1,#{line_width}})(?:[^\S\n]+\n?|\n*\Z|\n)|\n/
|
332
|
+
|
333
|
+
text.gsub(pattern, "\\1#{break_sequence}").chomp!(break_sequence)
|
268
334
|
end
|
269
335
|
|
270
336
|
# Returns +text+ transformed into HTML using simple formatting rules.
|
@@ -279,6 +345,7 @@ module ActionView
|
|
279
345
|
#
|
280
346
|
# ==== Options
|
281
347
|
# * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
|
348
|
+
# * <tt>:sanitize_options</tt> - Any extra options you want appended to the sanitize.
|
282
349
|
# * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>
|
283
350
|
#
|
284
351
|
# ==== Examples
|
@@ -303,10 +370,13 @@ module ActionView
|
|
303
370
|
#
|
304
371
|
# simple_format("<blink>Blinkable!</blink> It's true.", {}, sanitize: false)
|
305
372
|
# # => "<p><blink>Blinkable!</blink> It's true.</p>"
|
373
|
+
#
|
374
|
+
# simple_format("<a target=\"_blank\" href=\"http://example.com\">Continue</a>", {}, { sanitize_options: { attributes: %w[target href] } })
|
375
|
+
# # => "<p><a target=\"_blank\" href=\"http://example.com\">Continue</a></p>"
|
306
376
|
def simple_format(text, html_options = {}, options = {})
|
307
|
-
wrapper_tag = options
|
377
|
+
wrapper_tag = options[:wrapper_tag] || "p"
|
308
378
|
|
309
|
-
text = sanitize(text) if options.fetch(:sanitize, true)
|
379
|
+
text = sanitize(text, options.fetch(:sanitize_options, {})) if options.fetch(:sanitize, true)
|
310
380
|
paragraphs = split_paragraphs(text)
|
311
381
|
|
312
382
|
if paragraphs.empty?
|
@@ -318,7 +388,7 @@ module ActionView
|
|
318
388
|
end
|
319
389
|
end
|
320
390
|
|
321
|
-
# Creates a Cycle object whose
|
391
|
+
# Creates a Cycle object whose +to_s+ method cycles through elements of an
|
322
392
|
# array every time it is called. This can be used for example, to alternate
|
323
393
|
# classes for table rows. You can use named cycles to allow nesting in loops.
|
324
394
|
# Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
|
@@ -327,8 +397,8 @@ module ActionView
|
|
327
397
|
# and passing the name of the cycle. The current cycle string can be obtained
|
328
398
|
# anytime using the current_cycle method.
|
329
399
|
#
|
330
|
-
#
|
331
|
-
# @items = [1,2,3,4]
|
400
|
+
# <%# Alternate CSS classes for even and odd numbers... %>
|
401
|
+
# <% @items = [1,2,3,4] %>
|
332
402
|
# <table>
|
333
403
|
# <% @items.each do |item| %>
|
334
404
|
# <tr class="<%= cycle("odd", "even") -%>">
|
@@ -338,10 +408,12 @@ module ActionView
|
|
338
408
|
# </table>
|
339
409
|
#
|
340
410
|
#
|
341
|
-
#
|
342
|
-
# @items =
|
343
|
-
#
|
344
|
-
#
|
411
|
+
# <%# Cycle CSS classes for rows, and text colors for values within each row %>
|
412
|
+
# <% @items = [
|
413
|
+
# { first: "Robert", middle: "Daniel", last: "James" },
|
414
|
+
# { first: "Emily", middle: "Shannon", maiden: "Pike", last: "Hicks" },
|
415
|
+
# { first: "June", middle: "Dae", last: "Jones" },
|
416
|
+
# ] %>
|
345
417
|
# <% @items.each do |item| %>
|
346
418
|
# <tr class="<%= cycle("odd", "even", name: "row_class") -%>">
|
347
419
|
# <td>
|
@@ -372,8 +444,8 @@ module ActionView
|
|
372
444
|
# for complex table highlighting or any other design need which requires
|
373
445
|
# the current cycle string in more than one place.
|
374
446
|
#
|
375
|
-
#
|
376
|
-
# @items = [1,2,3,4]
|
447
|
+
# <%# Alternate background colors %>
|
448
|
+
# <% @items = [1,2,3,4] %>
|
377
449
|
# <% @items.each do |item| %>
|
378
450
|
# <div style="background-color:<%= cycle("red","white","blue") %>">
|
379
451
|
# <span style="background-color:<%= current_cycle %>"><%= item %></span>
|
@@ -387,8 +459,8 @@ module ActionView
|
|
387
459
|
# Resets a cycle so that it starts from the first element the next time
|
388
460
|
# it is called. Pass in +name+ to reset a named cycle.
|
389
461
|
#
|
390
|
-
#
|
391
|
-
# @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
|
462
|
+
# <%# Alternate CSS classes for even and odd numbers... %>
|
463
|
+
# <% @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]] %>
|
392
464
|
# <table>
|
393
465
|
# <% @items.each do |item| %>
|
394
466
|
# <tr class="<%= cycle("even", "odd") -%>">
|
@@ -4,14 +4,14 @@ require "action_view/helpers/tag_helper"
|
|
4
4
|
require "active_support/html_safe_translation"
|
5
5
|
|
6
6
|
module ActionView
|
7
|
-
# = Action View Translation Helpers
|
8
7
|
module Helpers # :nodoc:
|
8
|
+
# = Action View Translation \Helpers
|
9
9
|
module TranslationHelper
|
10
10
|
extend ActiveSupport::Concern
|
11
11
|
|
12
12
|
include TagHelper
|
13
13
|
|
14
|
-
# Specify whether an error should be raised for missing translations
|
14
|
+
# Specify whether an error should be raised for missing translations.
|
15
15
|
singleton_class.attr_accessor :raise_on_missing_translations
|
16
16
|
|
17
17
|
included do
|
@@ -93,7 +93,7 @@ module ActionView
|
|
93
93
|
break translated unless translated == MISSING_TRANSLATION
|
94
94
|
|
95
95
|
if alternatives.present? && !alternatives.first.is_a?(Symbol)
|
96
|
-
break alternatives.first && I18n.translate(**options, default: alternatives)
|
96
|
+
break alternatives.first && I18n.translate(nil, **options, default: alternatives)
|
97
97
|
end
|
98
98
|
|
99
99
|
first_key ||= key
|
@@ -3,11 +3,13 @@
|
|
3
3
|
require "active_support/core_ext/array/access"
|
4
4
|
require "active_support/core_ext/hash/keys"
|
5
5
|
require "active_support/core_ext/string/output_safety"
|
6
|
+
require "action_view/helpers/content_exfiltration_prevention_helper"
|
6
7
|
require "action_view/helpers/tag_helper"
|
7
8
|
|
8
9
|
module ActionView
|
9
|
-
# = Action View URL Helpers
|
10
10
|
module Helpers # :nodoc:
|
11
|
+
# = Action View URL \Helpers
|
12
|
+
#
|
11
13
|
# Provides a set of methods for making links and getting URLs that
|
12
14
|
# depend on the routing subsystem (see ActionDispatch::Routing).
|
13
15
|
# This allows you to use the same format for links in views
|
@@ -22,6 +24,7 @@ module ActionView
|
|
22
24
|
extend ActiveSupport::Concern
|
23
25
|
|
24
26
|
include TagHelper
|
27
|
+
include ContentExfiltrationPreventionHelper
|
25
28
|
|
26
29
|
module ClassMethods
|
27
30
|
def _url_for_modules
|
@@ -93,7 +96,7 @@ module ActionView
|
|
93
96
|
# ==== Examples
|
94
97
|
#
|
95
98
|
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
|
96
|
-
# and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
|
99
|
+
# and newer RESTful routes. Current \Rails style favors RESTful routes whenever possible, so base
|
97
100
|
# your application on resources and use
|
98
101
|
#
|
99
102
|
# link_to "Profile", profile_path(@profile)
|
@@ -170,9 +173,31 @@ module ActionView
|
|
170
173
|
# link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
|
171
174
|
# # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
|
172
175
|
#
|
173
|
-
# ====
|
176
|
+
# ==== Turbo
|
174
177
|
#
|
175
|
-
#
|
178
|
+
# Rails 7 ships with Turbo enabled by default. Turbo provides the following +:data+ options:
|
179
|
+
#
|
180
|
+
# * <tt>turbo_method: symbol of HTTP verb</tt> - Performs a Turbo link visit
|
181
|
+
# with the given HTTP verb. Forms are recommended when performing non-+GET+ requests.
|
182
|
+
# Only use <tt>data-turbo-method</tt> where a form is not possible.
|
183
|
+
#
|
184
|
+
# * <tt>turbo_confirm: "question?"</tt> - Adds a confirmation dialog to the link with the
|
185
|
+
# given value.
|
186
|
+
#
|
187
|
+
# {Consult the Turbo Handbook for more information on the options
|
188
|
+
# above.}[https://turbo.hotwired.dev/handbook/drive#performing-visits-with-a-different-method]
|
189
|
+
#
|
190
|
+
# ===== \Examples
|
191
|
+
#
|
192
|
+
# link_to "Delete profile", @profile, data: { turbo_method: :delete }
|
193
|
+
# # => <a href="/profiles/1" data-turbo-method="delete">Delete profile</a>
|
194
|
+
#
|
195
|
+
# link_to "Visit Other Site", "https://rubyonrails.org/", data: { turbo_confirm: "Are you sure?" }
|
196
|
+
# # => <a href="https://rubyonrails.org/" data-turbo-confirm="Are you sure?">Visit Other Site</a>
|
197
|
+
#
|
198
|
+
# ==== Deprecated: \Rails UJS Attributes
|
199
|
+
#
|
200
|
+
# Prior to \Rails 7, \Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following \Rails 7,
|
176
201
|
# this library is no longer on by default. This library integrated with the following options:
|
177
202
|
#
|
178
203
|
# * <tt>method: symbol of HTTP verb</tt> - This modifier will dynamically
|
@@ -198,7 +223,7 @@ module ActionView
|
|
198
223
|
# * <tt>:disable_with</tt> - Value of this parameter will be used as the
|
199
224
|
# name for a disabled version of the link.
|
200
225
|
#
|
201
|
-
# ===== Rails UJS Examples
|
226
|
+
# ===== \Rails UJS Examples
|
202
227
|
#
|
203
228
|
# link_to "Remove Profile", profile_path(@profile), method: :delete
|
204
229
|
# # => <a href="/profiles/1" rel="nofollow" data-method="delete">Remove Profile</a>
|
@@ -221,9 +246,6 @@ module ActionView
|
|
221
246
|
# Generates a form containing a single button that submits to the URL created
|
222
247
|
# by the set of +options+. This is the safest method to ensure links that
|
223
248
|
# cause changes to your data are not triggered by search bots or accelerators.
|
224
|
-
# If the HTML button does not work with your layout, you can also consider
|
225
|
-
# using the +link_to+ method with the <tt>:method</tt> modifier as described in
|
226
|
-
# the +link_to+ documentation.
|
227
249
|
#
|
228
250
|
# You can control the form and button behavior with +html_options+. Most
|
229
251
|
# values in +html_options+ are passed through to the button element. For
|
@@ -237,6 +259,10 @@ module ActionView
|
|
237
259
|
# The form submits a POST request by default. You can specify a different
|
238
260
|
# HTTP verb via the +:method+ option within +html_options+.
|
239
261
|
#
|
262
|
+
# If the HTML button generated from +button_to+ does not work with your layout, you can
|
263
|
+
# consider using the +link_to+ method with the +data-turbo-method+
|
264
|
+
# attribute as described in the +link_to+ documentation.
|
265
|
+
#
|
240
266
|
# ==== Options
|
241
267
|
# The +options+ hash accepts the same options as +url_for+. To generate a
|
242
268
|
# <tt><form></tt> element without an <tt>[action]</tt> attribute, pass
|
@@ -302,9 +328,9 @@ module ActionView
|
|
302
328
|
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
303
329
|
# # </form>"
|
304
330
|
#
|
305
|
-
# ==== Deprecated: Rails UJS Attributes
|
331
|
+
# ==== Deprecated: \Rails UJS Attributes
|
306
332
|
#
|
307
|
-
# Prior to Rails 7, Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following Rails 7,
|
333
|
+
# Prior to \Rails 7, \Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following \Rails 7,
|
308
334
|
# this library is no longer on by default. This library integrated with the following options:
|
309
335
|
#
|
310
336
|
# * <tt>:remote</tt> - If set to true, will allow <tt>@rails/ujs</tt> to control the
|
@@ -320,7 +346,7 @@ module ActionView
|
|
320
346
|
# used as the value for a disabled version of the submit
|
321
347
|
# button when the form is submitted.
|
322
348
|
#
|
323
|
-
# ===== Rails UJS Examples
|
349
|
+
# ===== \Rails UJS Examples
|
324
350
|
#
|
325
351
|
# <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
|
326
352
|
# # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
|
@@ -380,7 +406,8 @@ module ActionView
|
|
380
406
|
autocomplete: "off")
|
381
407
|
end
|
382
408
|
end
|
383
|
-
content_tag("form", inner_tags, form_options)
|
409
|
+
html = content_tag("form", inner_tags, form_options)
|
410
|
+
prevent_content_exfiltration(html)
|
384
411
|
end
|
385
412
|
|
386
413
|
# Creates a link tag of the given +name+ using a URL created by the set of
|
@@ -495,7 +522,7 @@ module ActionView
|
|
495
522
|
# * <tt>:reply_to</tt> - Preset the +Reply-To+ field of the email.
|
496
523
|
#
|
497
524
|
# ==== Obfuscation
|
498
|
-
# Prior to Rails 4.0, +mail_to+ provided options for encoding the address
|
525
|
+
# Prior to \Rails 4.0, +mail_to+ provided options for encoding the address
|
499
526
|
# in order to hinder email harvesters. To take advantage of these options,
|
500
527
|
# install the +actionview-encoded_mail_to+ gem.
|
501
528
|
#
|
@@ -534,6 +561,8 @@ module ActionView
|
|
534
561
|
content_tag("a", name || email_address, html_options, &block)
|
535
562
|
end
|
536
563
|
|
564
|
+
RFC2396_PARSER = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new
|
565
|
+
|
537
566
|
# True if the current request URI was generated by the given +options+.
|
538
567
|
#
|
539
568
|
# ==== Examples
|
@@ -590,14 +619,14 @@ module ActionView
|
|
590
619
|
|
591
620
|
options ||= options_as_kwargs
|
592
621
|
check_parameters ||= options.is_a?(Hash) && options.delete(:check_parameters)
|
593
|
-
url_string =
|
622
|
+
url_string = RFC2396_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY)
|
594
623
|
|
595
624
|
# We ignore any extra parameters in the request_uri if the
|
596
625
|
# submitted URL doesn't have any either. This lets the function
|
597
626
|
# work with things like ?order=asc
|
598
|
-
# the
|
627
|
+
# the behavior can be disabled with check_parameters: true
|
599
628
|
request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
|
600
|
-
request_uri =
|
629
|
+
request_uri = RFC2396_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)
|
601
630
|
|
602
631
|
if %r{^\w+://}.match?(url_string)
|
603
632
|
request_uri = +"#{request.protocol}#{request.host_with_port}#{request_uri}"
|
@@ -757,7 +786,7 @@ module ActionView
|
|
757
786
|
end
|
758
787
|
|
759
788
|
def add_method_to_attributes!(html_options, method)
|
760
|
-
if method_not_get_method?(method) && !html_options["rel"]
|
789
|
+
if method_not_get_method?(method) && !html_options["rel"].to_s.include?("nofollow")
|
761
790
|
if html_options["rel"].blank?
|
762
791
|
html_options["rel"] = "nofollow"
|
763
792
|
else
|
data/lib/action_view/helpers.rb
CHANGED
@@ -12,6 +12,7 @@ require "action_view/helpers/asset_tag_helper"
|
|
12
12
|
require "action_view/helpers/asset_url_helper"
|
13
13
|
require "action_view/helpers/atom_feed_helper"
|
14
14
|
require "action_view/helpers/cache_helper"
|
15
|
+
require "action_view/helpers/content_exfiltration_prevention_helper"
|
15
16
|
require "action_view/helpers/controller_helper"
|
16
17
|
require "action_view/helpers/csp_helper"
|
17
18
|
require "action_view/helpers/csrf_helper"
|
@@ -45,6 +46,7 @@ module ActionView # :nodoc:
|
|
45
46
|
include AtomFeedHelper
|
46
47
|
include CacheHelper
|
47
48
|
include CaptureHelper
|
49
|
+
include ContentExfiltrationPreventionHelper
|
48
50
|
include ControllerHelper
|
49
51
|
include CspHelper
|
50
52
|
include CsrfHelper
|