formtastic 1.0.0.beta → 1.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +4 -0
- data/lib/formtastic.rb +22 -11
- data/lib/formtastic/util.rb +5 -2
- data/spec/input_spec.rb +17 -0
- data/spec/inputs/check_boxes_input_spec.rb +25 -2
- data/spec/inputs/radio_input_spec.rb +24 -0
- data/spec/label_spec.rb +39 -0
- data/spec/spec_helper.rb +4 -0
- metadata +3 -3
data/README.textile
CHANGED
@@ -517,7 +517,11 @@ If you want to add your own input types to encapsulate your own logic or interfa
|
|
517
517
|
|
518
518
|
@Formtastic::SemanticFormHelper.builder = MyCustomBuilder@
|
519
519
|
|
520
|
+
h2. Security
|
520
521
|
|
522
|
+
By default formtastic escapes html entities in both labels and hints unless a string is marked as html_safe. If you are using an older rails version which doesn't know html_safe, or you want to globally turn this feature off, you can set the following in your initializer:
|
523
|
+
|
524
|
+
Formtastic::SemanticFormBuilder.escape_html_entities_in_hints_and_labels = false
|
521
525
|
|
522
526
|
|
523
527
|
h2. Status
|
data/lib/formtastic.rb
CHANGED
@@ -19,11 +19,12 @@ module Formtastic #:nodoc:
|
|
19
19
|
@@file_methods = [ :file?, :public_filename, :filename ]
|
20
20
|
@@priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
|
21
21
|
@@i18n_lookups_by_default = false
|
22
|
+
@@escape_html_entities_in_hints_and_labels = true
|
22
23
|
@@default_commit_button_accesskey = nil
|
23
24
|
|
24
25
|
cattr_accessor :default_text_field_size, :default_text_area_height, :all_fields_required_by_default, :include_blank_for_select_by_default,
|
25
26
|
:required_string, :optional_string, :inline_errors, :label_str_method, :collection_label_methods,
|
26
|
-
:inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default, :default_commit_button_accesskey
|
27
|
+
:inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default, :escape_html_entities_in_hints_and_labels, :default_commit_button_accesskey
|
27
28
|
|
28
29
|
RESERVED_COLUMNS = [:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]
|
29
30
|
|
@@ -478,7 +479,7 @@ module Formtastic #:nodoc:
|
|
478
479
|
# Collects association columns (relation columns) for the current form object class.
|
479
480
|
#
|
480
481
|
def association_columns(*by_associations) #:nodoc:
|
481
|
-
if @object.present?
|
482
|
+
if @object.present? && @object.class.respond_to?(:reflections)
|
482
483
|
@object.class.reflections.collect do |name, _|
|
483
484
|
if by_associations.present?
|
484
485
|
name if by_associations.include?(_.macro)
|
@@ -878,7 +879,7 @@ module Formtastic #:nodoc:
|
|
878
879
|
html_options[:checked] = selected_value == value if selected_option_is_present
|
879
880
|
|
880
881
|
li_content = template.content_tag(:label,
|
881
|
-
Formtastic::Util.html_safe("#{self.radio_button(input_name, value, html_options)} #{label}"),
|
882
|
+
Formtastic::Util.html_safe("#{self.radio_button(input_name, value, html_options)} #{escape_html_entities(label)}"),
|
882
883
|
:for => input_id
|
883
884
|
)
|
884
885
|
|
@@ -888,9 +889,9 @@ module Formtastic #:nodoc:
|
|
888
889
|
|
889
890
|
template.content_tag(:fieldset,
|
890
891
|
template.content_tag(:legend,
|
891
|
-
template.label_tag(nil, localized_string(method,
|
892
|
+
template.label_tag(nil, localized_string(method, options[:label], :label) || humanized_attribute_name(method), :for => nil), :class => :label
|
892
893
|
) <<
|
893
|
-
template.content_tag(:ol, list_item_content)
|
894
|
+
template.content_tag(:ol, Formtastic::Util.html_safe(list_item_content.join))
|
894
895
|
)
|
895
896
|
end
|
896
897
|
alias :boolean_radio_input :radio_input
|
@@ -1149,7 +1150,7 @@ module Formtastic #:nodoc:
|
|
1149
1150
|
html_options[:id] = input_id
|
1150
1151
|
|
1151
1152
|
li_content = template.content_tag(:label,
|
1152
|
-
Formtastic::Util.html_safe("#{self.check_box(input_name, html_options, value, unchecked_value)} #{label}"),
|
1153
|
+
Formtastic::Util.html_safe("#{self.check_box(input_name, html_options, value, unchecked_value)} #{escape_html_entities(label)}"),
|
1153
1154
|
:for => input_id
|
1154
1155
|
)
|
1155
1156
|
|
@@ -1159,9 +1160,9 @@ module Formtastic #:nodoc:
|
|
1159
1160
|
|
1160
1161
|
template.content_tag(:fieldset,
|
1161
1162
|
template.content_tag(:legend,
|
1162
|
-
template.label_tag(nil, localized_string(method,
|
1163
|
+
template.label_tag(nil, localized_string(method, options[:label], :label) || humanized_attribute_name(method), :for => nil), :class => :label
|
1163
1164
|
) <<
|
1164
|
-
template.content_tag(:ol, list_item_content)
|
1165
|
+
template.content_tag(:ol, Formtastic::Util.html_safe(list_item_content.join))
|
1165
1166
|
)
|
1166
1167
|
end
|
1167
1168
|
|
@@ -1223,7 +1224,7 @@ module Formtastic #:nodoc:
|
|
1223
1224
|
#
|
1224
1225
|
def inline_hints_for(method, options) #:nodoc:
|
1225
1226
|
options[:hint] = localized_string(method, options[:hint], :hint)
|
1226
|
-
return if options[:hint].blank?
|
1227
|
+
return if options[:hint].blank? or options[:hint].kind_of? Hash
|
1227
1228
|
template.content_tag(:p, Formtastic::Util.html_safe(options[:hint]), :class => 'inline-hints')
|
1228
1229
|
end
|
1229
1230
|
|
@@ -1401,7 +1402,8 @@ module Formtastic #:nodoc:
|
|
1401
1402
|
|
1402
1403
|
# Return if we have an Array of strings, fixnums or arrays
|
1403
1404
|
return collection if (collection.instance_of?(Array) || collection.instance_of?(Range)) &&
|
1404
|
-
[Array, Fixnum, String, Symbol].include?(collection.first.class)
|
1405
|
+
[Array, Fixnum, String, Symbol].include?(collection.first.class) &&
|
1406
|
+
!(options.include?(:label_method) || options.include?(:value_method))
|
1405
1407
|
|
1406
1408
|
label, value = detect_label_and_value_method!(collection, options)
|
1407
1409
|
collection.map { |o| [send_or_call(label, o), send_or_call(value, o)] }
|
@@ -1622,7 +1624,7 @@ module Formtastic #:nodoc:
|
|
1622
1624
|
key = value if value.is_a?(::Symbol)
|
1623
1625
|
|
1624
1626
|
if value.is_a?(::String)
|
1625
|
-
value
|
1627
|
+
escape_html_entities(value)
|
1626
1628
|
else
|
1627
1629
|
use_i18n = value.nil? ? @@i18n_lookups_by_default : (value != false)
|
1628
1630
|
|
@@ -1644,6 +1646,7 @@ module Formtastic #:nodoc:
|
|
1644
1646
|
|
1645
1647
|
i18n_value = ::Formtastic::I18n.t(defaults.shift,
|
1646
1648
|
options.merge(:default => defaults, :scope => type.to_s.pluralize.to_sym))
|
1649
|
+
i18n_value = escape_html_entities(i18n_value) if i18n_value.is_a?(::String)
|
1647
1650
|
i18n_value.blank? ? nil : i18n_value
|
1648
1651
|
end
|
1649
1652
|
end
|
@@ -1676,6 +1679,14 @@ module Formtastic #:nodoc:
|
|
1676
1679
|
options
|
1677
1680
|
end
|
1678
1681
|
|
1682
|
+
def escape_html_entities(string) #:nodoc:
|
1683
|
+
if @@escape_html_entities_in_hints_and_labels
|
1684
|
+
# Acceppt html_safe flag as indicator to skip escaping
|
1685
|
+
string = template.escape_once(string) unless string.respond_to?(:html_safe?) && string.html_safe? == true
|
1686
|
+
end
|
1687
|
+
string
|
1688
|
+
end
|
1689
|
+
|
1679
1690
|
end
|
1680
1691
|
|
1681
1692
|
# Wrappers around form_for (etc) with :builder => SemanticFormBuilder.
|
data/lib/formtastic/util.rb
CHANGED
@@ -18,8 +18,11 @@ module Formtastic
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def rails_safe_buffer_class
|
21
|
-
|
22
|
-
|
21
|
+
# It's important that we check ActiveSupport first,
|
22
|
+
# because in Rails 2.3.6 ActionView::SafeBuffer exists
|
23
|
+
# but is a deprecated proxy object.
|
24
|
+
return ActiveSupport::SafeBuffer if defined?(ActiveSupport::SafeBuffer)
|
25
|
+
return ActionView::SafeBuffer
|
23
26
|
end
|
24
27
|
|
25
28
|
end
|
data/spec/input_spec.rb
CHANGED
@@ -571,6 +571,23 @@ describe 'SemanticFormBuilder#input' do
|
|
571
571
|
end
|
572
572
|
end
|
573
573
|
|
574
|
+
describe 'when localized hint (I18n) is a model with attribute hints' do
|
575
|
+
it "should see the provided hash as a blank entry" do
|
576
|
+
::I18n.backend.store_translations :en,
|
577
|
+
:formtastic => {
|
578
|
+
:hints => {
|
579
|
+
:title => { # movie title
|
580
|
+
:summary => @localized_hint_text # summary of movie
|
581
|
+
}
|
582
|
+
}
|
583
|
+
}
|
584
|
+
semantic_form_for(@new_post) do |builder|
|
585
|
+
concat(builder.input(:title, :hint => true))
|
586
|
+
end
|
587
|
+
output_buffer.should_not have_tag('form li p.inline-hints', @localized_hint_text)
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
574
591
|
describe 'when localized hint (I18n) is not provided' do
|
575
592
|
it 'should not render a hint paragraph' do
|
576
593
|
semantic_form_for(@new_post) do |builder|
|
@@ -104,6 +104,16 @@ describe 'check_boxes input' do
|
|
104
104
|
output_buffer.should have_tag("form li fieldset ol li label input[@name='project[author_id][]']")
|
105
105
|
end
|
106
106
|
end
|
107
|
+
|
108
|
+
it 'should html escape the label string' do
|
109
|
+
output_buffer.replace ''
|
110
|
+
semantic_form_for(:project, :url => 'http://test.host') do |builder|
|
111
|
+
concat(builder.input(:author_id, :as => :check_boxes, :collection => [["<b>Item 1</b>", 1], ["<b>Item 2</b>", 2]]))
|
112
|
+
end
|
113
|
+
output_buffer.should have_tag('form li fieldset ol li label') do |label|
|
114
|
+
label.body.should match /<b>Item [12]<\/b>$/
|
115
|
+
end
|
116
|
+
end
|
107
117
|
end
|
108
118
|
|
109
119
|
describe 'when :selected is set' do
|
@@ -236,6 +246,7 @@ describe 'check_boxes input' do
|
|
236
246
|
|
237
247
|
before do
|
238
248
|
::I18n.backend.store_translations :en, :formtastic => { :labels => { :post => { :authors => "Translated!" }}}
|
249
|
+
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
|
239
250
|
|
240
251
|
@new_post.stub!(:author_ids).and_return(nil)
|
241
252
|
semantic_form_for(@new_post) do |builder|
|
@@ -245,6 +256,7 @@ describe 'check_boxes input' do
|
|
245
256
|
|
246
257
|
after do
|
247
258
|
::I18n.backend.reload!
|
259
|
+
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
|
248
260
|
end
|
249
261
|
|
250
262
|
it "should do foo" do
|
@@ -252,8 +264,19 @@ describe 'check_boxes input' do
|
|
252
264
|
end
|
253
265
|
|
254
266
|
end
|
255
|
-
|
256
|
-
end
|
257
267
|
|
268
|
+
describe "when :label option is set" do
|
269
|
+
before do
|
270
|
+
@new_post.stub!(:author_ids).and_return(nil)
|
271
|
+
semantic_form_for(@new_post) do |builder|
|
272
|
+
concat(builder.input(:authors, :as => :check_boxes, :label => 'The authors'))
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should output the correct label title" do
|
277
|
+
output_buffer.should have_tag("legend.label label", /The authors/)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
258
281
|
end
|
259
282
|
|
@@ -111,6 +111,16 @@ describe 'radio input' do
|
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
|
+
it 'should html escape the label string' do
|
115
|
+
output_buffer.replace ''
|
116
|
+
semantic_form_for(:project, :url => 'http://test.host') do |builder|
|
117
|
+
concat(builder.input(:author_id, :as => :radio, :collection => [["<b>Item 1</b>", 1], ["<b>Item 2</b>", 2]]))
|
118
|
+
end
|
119
|
+
output_buffer.should have_tag('form li fieldset ol li label') do |label|
|
120
|
+
label.body.should match /<b>Item [12]<\/b>$/
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
114
124
|
it 'should generate inputs for each item' do
|
115
125
|
::Author.find(:all).each do |author|
|
116
126
|
output_buffer.should have_tag("form li fieldset ol li label input#project_author_id_#{author.id}")
|
@@ -167,6 +177,7 @@ describe 'radio input' do
|
|
167
177
|
before do
|
168
178
|
::I18n.backend.store_translations :en, :formtastic => { :labels => { :post => { :authors => "Translated!" }}}
|
169
179
|
|
180
|
+
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
|
170
181
|
@new_post.stub!(:author_ids).and_return(nil)
|
171
182
|
semantic_form_for(@new_post) do |builder|
|
172
183
|
concat(builder.input(:authors, :as => :radio))
|
@@ -175,6 +186,7 @@ describe 'radio input' do
|
|
175
186
|
|
176
187
|
after do
|
177
188
|
::I18n.backend.reload!
|
189
|
+
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
|
178
190
|
end
|
179
191
|
|
180
192
|
it "should do foo" do
|
@@ -183,4 +195,16 @@ describe 'radio input' do
|
|
183
195
|
|
184
196
|
end
|
185
197
|
|
198
|
+
describe "when :label option is set" do
|
199
|
+
before do
|
200
|
+
@new_post.stub!(:author_ids).and_return(nil)
|
201
|
+
@form = semantic_form_for(@new_post) do |builder|
|
202
|
+
concat(builder.input(:authors, :as => :radio, :label => 'The authors'))
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should output the correct label title" do
|
207
|
+
output_buffer.should have_tag("legend.label label", /The authors/)
|
208
|
+
end
|
209
|
+
end
|
186
210
|
end
|
data/spec/label_spec.rb
CHANGED
@@ -30,6 +30,24 @@ describe 'SemanticFormBuilder#label' do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
describe 'when a collection is given' do
|
34
|
+
it 'should use a supplied label_method for simple collections' do
|
35
|
+
semantic_form_for(:project, :url => 'http://test.host') do |builder|
|
36
|
+
concat(builder.input(:author_id, :as => :check_boxes, :collection => [:a, :b, :c], :value_method => :to_s, :label_method => proc {|f| ('Label_%s' % [f])}))
|
37
|
+
end
|
38
|
+
output_buffer.should have_tag('form li fieldset ol li label', :with => /Label_[abc]/, :count => 3)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should use a supplied value_method for simple collections' do
|
42
|
+
semantic_form_for(:project, :url => 'http://test.host') do |builder|
|
43
|
+
concat(builder.input(:author_id, :as => :check_boxes, :collection => [:a, :b, :c], :value_method => proc {|f| ('Value_%s' % [f.to_s])}))
|
44
|
+
end
|
45
|
+
output_buffer.should have_tag('form li fieldset ol li label input[value="Value_a"]')
|
46
|
+
output_buffer.should have_tag('form li fieldset ol li label input[value="Value_b"]')
|
47
|
+
output_buffer.should have_tag('form li fieldset ol li label input[value="Value_c"]')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
33
51
|
describe 'when label is given' do
|
34
52
|
it 'should allow the text to be given as label option' do
|
35
53
|
semantic_form_for(@new_post) do |builder|
|
@@ -42,6 +60,27 @@ describe 'SemanticFormBuilder#label' do
|
|
42
60
|
builder.label(:login, :label => false).should be_blank
|
43
61
|
end
|
44
62
|
end
|
63
|
+
|
64
|
+
it 'should html escape the label string by default' do
|
65
|
+
semantic_form_for(@new_post) do |builder|
|
66
|
+
builder.label(:login, :required => false, :label => '<b>My label</b>').should == "<label for=\"post_login\"><b>My label</b></label>"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should not html escape the label if configured that way' do
|
71
|
+
::Formtastic::SemanticFormBuilder.escape_html_entities_in_hints_and_labels = false
|
72
|
+
semantic_form_for(@new_post) do |builder|
|
73
|
+
builder.label(:login, :required => false, :label => '<b>My label</b>').should == "<label for=\"post_login\"><b>My label</b></label>"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should not html escape the label string for html_safe strings' do
|
78
|
+
::Formtastic::SemanticFormBuilder.escape_html_entities_in_hints_and_labels = true
|
79
|
+
semantic_form_for(@new_post) do |builder|
|
80
|
+
builder.label(:login, :required => false, :label => '<b>My label</b>'.html_safe).should == "<label for=\"post_login\"><b>My label</b></label>"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
45
84
|
end
|
46
85
|
|
47
86
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
require 'rubygems'
|
3
3
|
|
4
|
+
gem 'i18n', '< 0.4'
|
5
|
+
|
4
6
|
gem 'activesupport', '2.3.8'
|
5
7
|
gem 'actionpack', '2.3.8'
|
6
8
|
require 'active_support'
|
@@ -8,6 +10,8 @@ require 'action_pack'
|
|
8
10
|
require 'action_view'
|
9
11
|
require 'action_controller'
|
10
12
|
|
13
|
+
|
14
|
+
|
11
15
|
gem 'rspec', '>= 1.2.6'
|
12
16
|
gem 'rspec-rails', '>= 1.2.6'
|
13
17
|
gem 'hpricot', '>= 0.6.1'
|
metadata
CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
|
|
6
6
|
- 1
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.0.
|
9
|
+
- beta2
|
10
|
+
version: 1.0.0.beta2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Justin French
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-07-21 00:00:00 +10:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|