##// END OF EJS Templates
Merged r14079 (#19316)....
Jean-Philippe Lang -
r13712:97fc28da3a56
parent child
Show More
@@ -1,283 +1,283
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class CustomField < ActiveRecord::Base
18 class CustomField < ActiveRecord::Base
19 include Redmine::SubclassFactory
19 include Redmine::SubclassFactory
20
20
21 has_many :custom_values, :dependent => :delete_all
21 has_many :custom_values, :dependent => :delete_all
22 has_and_belongs_to_many :roles, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "custom_field_id"
22 has_and_belongs_to_many :roles, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "custom_field_id"
23 acts_as_list :scope => 'type = \'#{self.class}\''
23 acts_as_list :scope => 'type = \'#{self.class}\''
24 serialize :possible_values
24 serialize :possible_values
25 store :format_store
25 store :format_store
26
26
27 validates_presence_of :name, :field_format
27 validates_presence_of :name, :field_format
28 validates_uniqueness_of :name, :scope => :type
28 validates_uniqueness_of :name, :scope => :type
29 validates_length_of :name, :maximum => 30
29 validates_length_of :name, :maximum => 30
30 validates_inclusion_of :field_format, :in => Proc.new { Redmine::FieldFormat.available_formats }
30 validates_inclusion_of :field_format, :in => Proc.new { Redmine::FieldFormat.available_formats }
31 validate :validate_custom_field
31 validate :validate_custom_field
32 attr_protected :id
32 attr_protected :id
33
33
34 before_validation :set_searchable
34 before_validation :set_searchable
35 before_save do |field|
35 before_save do |field|
36 field.format.before_custom_field_save(field)
36 field.format.before_custom_field_save(field)
37 end
37 end
38 after_save :handle_multiplicity_change
38 after_save :handle_multiplicity_change
39 after_save do |field|
39 after_save do |field|
40 if field.visible_changed? && field.visible
40 if field.visible_changed? && field.visible
41 field.roles.clear
41 field.roles.clear
42 end
42 end
43 end
43 end
44
44
45 scope :sorted, lambda { order(:position) }
45 scope :sorted, lambda { order(:position) }
46 scope :visible, lambda {|*args|
46 scope :visible, lambda {|*args|
47 user = args.shift || User.current
47 user = args.shift || User.current
48 if user.admin?
48 if user.admin?
49 # nop
49 # nop
50 elsif user.memberships.any?
50 elsif user.memberships.any?
51 where("#{table_name}.visible = ? OR #{table_name}.id IN (SELECT DISTINCT cfr.custom_field_id FROM #{Member.table_name} m" +
51 where("#{table_name}.visible = ? OR #{table_name}.id IN (SELECT DISTINCT cfr.custom_field_id FROM #{Member.table_name} m" +
52 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
52 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
53 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
53 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
54 " WHERE m.user_id = ?)",
54 " WHERE m.user_id = ?)",
55 true, user.id)
55 true, user.id)
56 else
56 else
57 where(:visible => true)
57 where(:visible => true)
58 end
58 end
59 }
59 }
60
60
61 def visible_by?(project, user=User.current)
61 def visible_by?(project, user=User.current)
62 visible? || user.admin?
62 visible? || user.admin?
63 end
63 end
64
64
65 def format
65 def format
66 @format ||= Redmine::FieldFormat.find(field_format)
66 @format ||= Redmine::FieldFormat.find(field_format)
67 end
67 end
68
68
69 def field_format=(arg)
69 def field_format=(arg)
70 # cannot change format of a saved custom field
70 # cannot change format of a saved custom field
71 if new_record?
71 if new_record?
72 @format = nil
72 @format = nil
73 super
73 super
74 end
74 end
75 end
75 end
76
76
77 def set_searchable
77 def set_searchable
78 # make sure these fields are not searchable
78 # make sure these fields are not searchable
79 self.searchable = false unless format.class.searchable_supported
79 self.searchable = false unless format.class.searchable_supported
80 # make sure only these fields can have multiple values
80 # make sure only these fields can have multiple values
81 self.multiple = false unless format.class.multiple_supported
81 self.multiple = false unless format.class.multiple_supported
82 true
82 true
83 end
83 end
84
84
85 def validate_custom_field
85 def validate_custom_field
86 format.validate_custom_field(self).each do |attribute, message|
86 format.validate_custom_field(self).each do |attribute, message|
87 errors.add attribute, message
87 errors.add attribute, message
88 end
88 end
89
89
90 if regexp.present?
90 if regexp.present?
91 begin
91 begin
92 Regexp.new(regexp)
92 Regexp.new(regexp)
93 rescue
93 rescue
94 errors.add(:regexp, :invalid)
94 errors.add(:regexp, :invalid)
95 end
95 end
96 end
96 end
97
97
98 if default_value.present?
98 if default_value.present?
99 validate_field_value(default_value).each do |message|
99 validate_field_value(default_value).each do |message|
100 errors.add :default_value, message
100 errors.add :default_value, message
101 end
101 end
102 end
102 end
103 end
103 end
104
104
105 def possible_custom_value_options(custom_value)
105 def possible_custom_value_options(custom_value)
106 format.possible_custom_value_options(custom_value)
106 format.possible_custom_value_options(custom_value)
107 end
107 end
108
108
109 def possible_values_options(object=nil)
109 def possible_values_options(object=nil)
110 if object.is_a?(Array)
110 if object.is_a?(Array)
111 object.map {|o| format.possible_values_options(self, o)}.reduce(:&) || []
111 object.map {|o| format.possible_values_options(self, o)}.reduce(:&) || []
112 else
112 else
113 format.possible_values_options(self, object) || []
113 format.possible_values_options(self, object) || []
114 end
114 end
115 end
115 end
116
116
117 def possible_values
117 def possible_values
118 values = read_attribute(:possible_values)
118 values = read_attribute(:possible_values)
119 if values.is_a?(Array)
119 if values.is_a?(Array)
120 values.each do |value|
120 values.each do |value|
121 value.force_encoding('UTF-8')
121 value.to_s.force_encoding('UTF-8')
122 end
122 end
123 values
123 values
124 else
124 else
125 []
125 []
126 end
126 end
127 end
127 end
128
128
129 # Makes possible_values accept a multiline string
129 # Makes possible_values accept a multiline string
130 def possible_values=(arg)
130 def possible_values=(arg)
131 if arg.is_a?(Array)
131 if arg.is_a?(Array)
132 values = arg.compact.collect(&:strip).select {|v| !v.blank?}
132 values = arg.compact.map {|a| a.to_s.strip}.reject(&:blank?)
133 write_attribute(:possible_values, values)
133 write_attribute(:possible_values, values)
134 else
134 else
135 self.possible_values = arg.to_s.split(/[\n\r]+/)
135 self.possible_values = arg.to_s.split(/[\n\r]+/)
136 end
136 end
137 end
137 end
138
138
139 def cast_value(value)
139 def cast_value(value)
140 format.cast_value(self, value)
140 format.cast_value(self, value)
141 end
141 end
142
142
143 def value_from_keyword(keyword, customized)
143 def value_from_keyword(keyword, customized)
144 possible_values_options = possible_values_options(customized)
144 possible_values_options = possible_values_options(customized)
145 if possible_values_options.present?
145 if possible_values_options.present?
146 keyword = keyword.to_s.downcase
146 keyword = keyword.to_s.downcase
147 if v = possible_values_options.detect {|text, id| text.downcase == keyword}
147 if v = possible_values_options.detect {|text, id| text.downcase == keyword}
148 if v.is_a?(Array)
148 if v.is_a?(Array)
149 v.last
149 v.last
150 else
150 else
151 v
151 v
152 end
152 end
153 end
153 end
154 else
154 else
155 keyword
155 keyword
156 end
156 end
157 end
157 end
158
158
159 # Returns a ORDER BY clause that can used to sort customized
159 # Returns a ORDER BY clause that can used to sort customized
160 # objects by their value of the custom field.
160 # objects by their value of the custom field.
161 # Returns nil if the custom field can not be used for sorting.
161 # Returns nil if the custom field can not be used for sorting.
162 def order_statement
162 def order_statement
163 return nil if multiple?
163 return nil if multiple?
164 format.order_statement(self)
164 format.order_statement(self)
165 end
165 end
166
166
167 # Returns a GROUP BY clause that can used to group by custom value
167 # Returns a GROUP BY clause that can used to group by custom value
168 # Returns nil if the custom field can not be used for grouping.
168 # Returns nil if the custom field can not be used for grouping.
169 def group_statement
169 def group_statement
170 return nil if multiple?
170 return nil if multiple?
171 format.group_statement(self)
171 format.group_statement(self)
172 end
172 end
173
173
174 def join_for_order_statement
174 def join_for_order_statement
175 format.join_for_order_statement(self)
175 format.join_for_order_statement(self)
176 end
176 end
177
177
178 def visibility_by_project_condition(project_key=nil, user=User.current, id_column=nil)
178 def visibility_by_project_condition(project_key=nil, user=User.current, id_column=nil)
179 if visible? || user.admin?
179 if visible? || user.admin?
180 "1=1"
180 "1=1"
181 elsif user.anonymous?
181 elsif user.anonymous?
182 "1=0"
182 "1=0"
183 else
183 else
184 project_key ||= "#{self.class.customized_class.table_name}.project_id"
184 project_key ||= "#{self.class.customized_class.table_name}.project_id"
185 id_column ||= id
185 id_column ||= id
186 "#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
186 "#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
187 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
187 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
188 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
188 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
189 " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id_column})"
189 " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id_column})"
190 end
190 end
191 end
191 end
192
192
193 def self.visibility_condition
193 def self.visibility_condition
194 if user.admin?
194 if user.admin?
195 "1=1"
195 "1=1"
196 elsif user.anonymous?
196 elsif user.anonymous?
197 "#{table_name}.visible"
197 "#{table_name}.visible"
198 else
198 else
199 "#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
199 "#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
200 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
200 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
201 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
201 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
202 " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id})"
202 " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id})"
203 end
203 end
204 end
204 end
205
205
206 def <=>(field)
206 def <=>(field)
207 position <=> field.position
207 position <=> field.position
208 end
208 end
209
209
210 # Returns the class that values represent
210 # Returns the class that values represent
211 def value_class
211 def value_class
212 format.target_class if format.respond_to?(:target_class)
212 format.target_class if format.respond_to?(:target_class)
213 end
213 end
214
214
215 def self.customized_class
215 def self.customized_class
216 self.name =~ /^(.+)CustomField$/
216 self.name =~ /^(.+)CustomField$/
217 $1.constantize rescue nil
217 $1.constantize rescue nil
218 end
218 end
219
219
220 # to move in project_custom_field
220 # to move in project_custom_field
221 def self.for_all
221 def self.for_all
222 where(:is_for_all => true).order('position').to_a
222 where(:is_for_all => true).order('position').to_a
223 end
223 end
224
224
225 def type_name
225 def type_name
226 nil
226 nil
227 end
227 end
228
228
229 # Returns the error messages for the given value
229 # Returns the error messages for the given value
230 # or an empty array if value is a valid value for the custom field
230 # or an empty array if value is a valid value for the custom field
231 def validate_custom_value(custom_value)
231 def validate_custom_value(custom_value)
232 value = custom_value.value
232 value = custom_value.value
233 errs = []
233 errs = []
234 if value.is_a?(Array)
234 if value.is_a?(Array)
235 if !multiple?
235 if !multiple?
236 errs << ::I18n.t('activerecord.errors.messages.invalid')
236 errs << ::I18n.t('activerecord.errors.messages.invalid')
237 end
237 end
238 if is_required? && value.detect(&:present?).nil?
238 if is_required? && value.detect(&:present?).nil?
239 errs << ::I18n.t('activerecord.errors.messages.blank')
239 errs << ::I18n.t('activerecord.errors.messages.blank')
240 end
240 end
241 else
241 else
242 if is_required? && value.blank?
242 if is_required? && value.blank?
243 errs << ::I18n.t('activerecord.errors.messages.blank')
243 errs << ::I18n.t('activerecord.errors.messages.blank')
244 end
244 end
245 end
245 end
246 errs += format.validate_custom_value(custom_value)
246 errs += format.validate_custom_value(custom_value)
247 errs
247 errs
248 end
248 end
249
249
250 # Returns the error messages for the default custom field value
250 # Returns the error messages for the default custom field value
251 def validate_field_value(value)
251 def validate_field_value(value)
252 validate_custom_value(CustomFieldValue.new(:custom_field => self, :value => value))
252 validate_custom_value(CustomFieldValue.new(:custom_field => self, :value => value))
253 end
253 end
254
254
255 # Returns true if value is a valid value for the custom field
255 # Returns true if value is a valid value for the custom field
256 def valid_field_value?(value)
256 def valid_field_value?(value)
257 validate_field_value(value).empty?
257 validate_field_value(value).empty?
258 end
258 end
259
259
260 def format_in?(*args)
260 def format_in?(*args)
261 args.include?(field_format)
261 args.include?(field_format)
262 end
262 end
263
263
264 protected
264 protected
265
265
266 # Removes multiple values for the custom field after setting the multiple attribute to false
266 # Removes multiple values for the custom field after setting the multiple attribute to false
267 # We kepp the value with the highest id for each customized object
267 # We kepp the value with the highest id for each customized object
268 def handle_multiplicity_change
268 def handle_multiplicity_change
269 if !new_record? && multiple_was && !multiple
269 if !new_record? && multiple_was && !multiple
270 ids = custom_values.
270 ids = custom_values.
271 where("EXISTS(SELECT 1 FROM #{CustomValue.table_name} cve WHERE cve.custom_field_id = #{CustomValue.table_name}.custom_field_id" +
271 where("EXISTS(SELECT 1 FROM #{CustomValue.table_name} cve WHERE cve.custom_field_id = #{CustomValue.table_name}.custom_field_id" +
272 " AND cve.customized_type = #{CustomValue.table_name}.customized_type AND cve.customized_id = #{CustomValue.table_name}.customized_id" +
272 " AND cve.customized_type = #{CustomValue.table_name}.customized_type AND cve.customized_id = #{CustomValue.table_name}.customized_id" +
273 " AND cve.id > #{CustomValue.table_name}.id)").
273 " AND cve.id > #{CustomValue.table_name}.id)").
274 pluck(:id)
274 pluck(:id)
275
275
276 if ids.any?
276 if ids.any?
277 custom_values.where(:id => ids).delete_all
277 custom_values.where(:id => ids).delete_all
278 end
278 end
279 end
279 end
280 end
280 end
281 end
281 end
282
282
283 require_dependency 'redmine/field_format'
283 require_dependency 'redmine/field_format'
@@ -1,321 +1,335
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class CustomFieldTest < ActiveSupport::TestCase
20 class CustomFieldTest < ActiveSupport::TestCase
21 fixtures :custom_fields, :roles, :projects,
21 fixtures :custom_fields, :roles, :projects,
22 :trackers, :issue_statuses,
22 :trackers, :issue_statuses,
23 :issues
23 :issues
24
24
25 def test_create
25 def test_create
26 field = UserCustomField.new(:name => 'Money money money', :field_format => 'float')
26 field = UserCustomField.new(:name => 'Money money money', :field_format => 'float')
27 assert field.save
27 assert field.save
28 end
28 end
29
29
30 def test_before_validation
30 def test_before_validation
31 field = CustomField.new(:name => 'test_before_validation', :field_format => 'int')
31 field = CustomField.new(:name => 'test_before_validation', :field_format => 'int')
32 field.searchable = true
32 field.searchable = true
33 assert field.save
33 assert field.save
34 assert_equal false, field.searchable
34 assert_equal false, field.searchable
35 field.searchable = true
35 field.searchable = true
36 assert field.save
36 assert field.save
37 assert_equal false, field.searchable
37 assert_equal false, field.searchable
38 end
38 end
39
39
40 def test_regexp_validation
40 def test_regexp_validation
41 field = IssueCustomField.new(:name => 'regexp', :field_format => 'text', :regexp => '[a-z0-9')
41 field = IssueCustomField.new(:name => 'regexp', :field_format => 'text', :regexp => '[a-z0-9')
42 assert !field.save
42 assert !field.save
43 assert_include I18n.t('activerecord.errors.messages.invalid'),
43 assert_include I18n.t('activerecord.errors.messages.invalid'),
44 field.errors[:regexp]
44 field.errors[:regexp]
45 field.regexp = '[a-z0-9]'
45 field.regexp = '[a-z0-9]'
46 assert field.save
46 assert field.save
47 end
47 end
48
48
49 def test_default_value_should_be_validated
49 def test_default_value_should_be_validated
50 field = CustomField.new(:name => 'Test', :field_format => 'int')
50 field = CustomField.new(:name => 'Test', :field_format => 'int')
51 field.default_value = 'abc'
51 field.default_value = 'abc'
52 assert !field.valid?
52 assert !field.valid?
53 field.default_value = '6'
53 field.default_value = '6'
54 assert field.valid?
54 assert field.valid?
55 end
55 end
56
56
57 def test_default_value_should_not_be_validated_when_blank
57 def test_default_value_should_not_be_validated_when_blank
58 field = CustomField.new(:name => 'Test', :field_format => 'list', :possible_values => ['a', 'b'], :is_required => true, :default_value => '')
58 field = CustomField.new(:name => 'Test', :field_format => 'list', :possible_values => ['a', 'b'], :is_required => true, :default_value => '')
59 assert field.valid?
59 assert field.valid?
60 end
60 end
61
61
62 def test_field_format_should_be_validated
62 def test_field_format_should_be_validated
63 field = CustomField.new(:name => 'Test', :field_format => 'foo')
63 field = CustomField.new(:name => 'Test', :field_format => 'foo')
64 assert !field.valid?
64 assert !field.valid?
65 end
65 end
66
66
67 def test_field_format_validation_should_accept_formats_added_at_runtime
67 def test_field_format_validation_should_accept_formats_added_at_runtime
68 Redmine::FieldFormat.add 'foobar', Class.new(Redmine::FieldFormat::Base)
68 Redmine::FieldFormat.add 'foobar', Class.new(Redmine::FieldFormat::Base)
69
69
70 field = CustomField.new(:name => 'Some Custom Field', :field_format => 'foobar')
70 field = CustomField.new(:name => 'Some Custom Field', :field_format => 'foobar')
71 assert field.valid?, 'field should be valid'
71 assert field.valid?, 'field should be valid'
72 ensure
72 ensure
73 Redmine::FieldFormat.delete 'foobar'
73 Redmine::FieldFormat.delete 'foobar'
74 end
74 end
75
75
76 def test_should_not_change_field_format_of_existing_custom_field
76 def test_should_not_change_field_format_of_existing_custom_field
77 field = CustomField.find(1)
77 field = CustomField.find(1)
78 field.field_format = 'int'
78 field.field_format = 'int'
79 assert_equal 'list', field.field_format
79 assert_equal 'list', field.field_format
80 end
80 end
81
81
82 def test_possible_values_should_accept_an_array
82 def test_possible_values_should_accept_an_array
83 field = CustomField.new
83 field = CustomField.new
84 field.possible_values = ["One value", ""]
84 field.possible_values = ["One value", ""]
85 assert_equal ["One value"], field.possible_values
85 assert_equal ["One value"], field.possible_values
86 end
86 end
87
87
88 def test_possible_values_should_stringify_values
89 field = CustomField.new
90 field.possible_values = [1, 2]
91 assert_equal ["1", "2"], field.possible_values
92 end
93
88 def test_possible_values_should_accept_a_string
94 def test_possible_values_should_accept_a_string
89 field = CustomField.new
95 field = CustomField.new
90 field.possible_values = "One value"
96 field.possible_values = "One value"
91 assert_equal ["One value"], field.possible_values
97 assert_equal ["One value"], field.possible_values
92 end
98 end
93
99
100 def test_possible_values_should_return_utf8_encoded_strings
101 field = CustomField.new
102 s = "Value".force_encoding('BINARY')
103 field.possible_values = s
104 assert_equal [s], field.possible_values
105 assert_equal 'UTF-8', field.possible_values.first.encoding.name
106 end
107
94 def test_possible_values_should_accept_a_multiline_string
108 def test_possible_values_should_accept_a_multiline_string
95 field = CustomField.new
109 field = CustomField.new
96 field.possible_values = "One value\nAnd another one \r\n \n"
110 field.possible_values = "One value\nAnd another one \r\n \n"
97 assert_equal ["One value", "And another one"], field.possible_values
111 assert_equal ["One value", "And another one"], field.possible_values
98 end
112 end
99
113
100 def test_possible_values_stored_as_binary_should_be_utf8_encoded
114 def test_possible_values_stored_as_binary_should_be_utf8_encoded
101 field = CustomField.find(11)
115 field = CustomField.find(11)
102 assert_kind_of Array, field.possible_values
116 assert_kind_of Array, field.possible_values
103 assert field.possible_values.size > 0
117 assert field.possible_values.size > 0
104 field.possible_values.each do |value|
118 field.possible_values.each do |value|
105 assert_equal "UTF-8", value.encoding.name
119 assert_equal "UTF-8", value.encoding.name
106 end
120 end
107 end
121 end
108
122
109 def test_destroy
123 def test_destroy
110 field = CustomField.find(1)
124 field = CustomField.find(1)
111 assert field.destroy
125 assert field.destroy
112 end
126 end
113
127
114 def test_new_subclass_instance_should_return_an_instance
128 def test_new_subclass_instance_should_return_an_instance
115 f = CustomField.new_subclass_instance('IssueCustomField')
129 f = CustomField.new_subclass_instance('IssueCustomField')
116 assert_kind_of IssueCustomField, f
130 assert_kind_of IssueCustomField, f
117 end
131 end
118
132
119 def test_new_subclass_instance_should_set_attributes
133 def test_new_subclass_instance_should_set_attributes
120 f = CustomField.new_subclass_instance('IssueCustomField', :name => 'Test')
134 f = CustomField.new_subclass_instance('IssueCustomField', :name => 'Test')
121 assert_kind_of IssueCustomField, f
135 assert_kind_of IssueCustomField, f
122 assert_equal 'Test', f.name
136 assert_equal 'Test', f.name
123 end
137 end
124
138
125 def test_new_subclass_instance_with_invalid_class_name_should_return_nil
139 def test_new_subclass_instance_with_invalid_class_name_should_return_nil
126 assert_nil CustomField.new_subclass_instance('WrongClassName')
140 assert_nil CustomField.new_subclass_instance('WrongClassName')
127 end
141 end
128
142
129 def test_new_subclass_instance_with_non_subclass_name_should_return_nil
143 def test_new_subclass_instance_with_non_subclass_name_should_return_nil
130 assert_nil CustomField.new_subclass_instance('Project')
144 assert_nil CustomField.new_subclass_instance('Project')
131 end
145 end
132
146
133 def test_string_field_validation_with_blank_value
147 def test_string_field_validation_with_blank_value
134 f = CustomField.new(:field_format => 'string')
148 f = CustomField.new(:field_format => 'string')
135
149
136 assert f.valid_field_value?(nil)
150 assert f.valid_field_value?(nil)
137 assert f.valid_field_value?('')
151 assert f.valid_field_value?('')
138
152
139 f.is_required = true
153 f.is_required = true
140 assert !f.valid_field_value?(nil)
154 assert !f.valid_field_value?(nil)
141 assert !f.valid_field_value?('')
155 assert !f.valid_field_value?('')
142 end
156 end
143
157
144 def test_string_field_validation_with_min_and_max_lengths
158 def test_string_field_validation_with_min_and_max_lengths
145 f = CustomField.new(:field_format => 'string', :min_length => 2, :max_length => 5)
159 f = CustomField.new(:field_format => 'string', :min_length => 2, :max_length => 5)
146
160
147 assert f.valid_field_value?(nil)
161 assert f.valid_field_value?(nil)
148 assert f.valid_field_value?('')
162 assert f.valid_field_value?('')
149 assert !f.valid_field_value?(' ')
163 assert !f.valid_field_value?(' ')
150 assert f.valid_field_value?('a' * 2)
164 assert f.valid_field_value?('a' * 2)
151 assert !f.valid_field_value?('a')
165 assert !f.valid_field_value?('a')
152 assert !f.valid_field_value?('a' * 6)
166 assert !f.valid_field_value?('a' * 6)
153 end
167 end
154
168
155 def test_string_field_validation_with_regexp
169 def test_string_field_validation_with_regexp
156 f = CustomField.new(:field_format => 'string', :regexp => '^[A-Z0-9]*$')
170 f = CustomField.new(:field_format => 'string', :regexp => '^[A-Z0-9]*$')
157
171
158 assert f.valid_field_value?(nil)
172 assert f.valid_field_value?(nil)
159 assert f.valid_field_value?('')
173 assert f.valid_field_value?('')
160 assert !f.valid_field_value?(' ')
174 assert !f.valid_field_value?(' ')
161 assert f.valid_field_value?('ABC')
175 assert f.valid_field_value?('ABC')
162 assert !f.valid_field_value?('abc')
176 assert !f.valid_field_value?('abc')
163 end
177 end
164
178
165 def test_date_field_validation
179 def test_date_field_validation
166 f = CustomField.new(:field_format => 'date')
180 f = CustomField.new(:field_format => 'date')
167
181
168 assert f.valid_field_value?(nil)
182 assert f.valid_field_value?(nil)
169 assert f.valid_field_value?('')
183 assert f.valid_field_value?('')
170 assert !f.valid_field_value?(' ')
184 assert !f.valid_field_value?(' ')
171 assert f.valid_field_value?('1975-07-14')
185 assert f.valid_field_value?('1975-07-14')
172 assert !f.valid_field_value?('1975-07-33')
186 assert !f.valid_field_value?('1975-07-33')
173 assert !f.valid_field_value?('abc')
187 assert !f.valid_field_value?('abc')
174 end
188 end
175
189
176 def test_list_field_validation
190 def test_list_field_validation
177 f = CustomField.new(:field_format => 'list', :possible_values => ['value1', 'value2'])
191 f = CustomField.new(:field_format => 'list', :possible_values => ['value1', 'value2'])
178
192
179 assert f.valid_field_value?(nil)
193 assert f.valid_field_value?(nil)
180 assert f.valid_field_value?('')
194 assert f.valid_field_value?('')
181 assert !f.valid_field_value?(' ')
195 assert !f.valid_field_value?(' ')
182 assert f.valid_field_value?('value2')
196 assert f.valid_field_value?('value2')
183 assert !f.valid_field_value?('abc')
197 assert !f.valid_field_value?('abc')
184 end
198 end
185
199
186 def test_int_field_validation
200 def test_int_field_validation
187 f = CustomField.new(:field_format => 'int')
201 f = CustomField.new(:field_format => 'int')
188
202
189 assert f.valid_field_value?(nil)
203 assert f.valid_field_value?(nil)
190 assert f.valid_field_value?('')
204 assert f.valid_field_value?('')
191 assert !f.valid_field_value?(' ')
205 assert !f.valid_field_value?(' ')
192 assert f.valid_field_value?('123')
206 assert f.valid_field_value?('123')
193 assert f.valid_field_value?('+123')
207 assert f.valid_field_value?('+123')
194 assert f.valid_field_value?('-123')
208 assert f.valid_field_value?('-123')
195 assert !f.valid_field_value?('6abc')
209 assert !f.valid_field_value?('6abc')
196 assert f.valid_field_value?(123)
210 assert f.valid_field_value?(123)
197 end
211 end
198
212
199 def test_float_field_validation
213 def test_float_field_validation
200 f = CustomField.new(:field_format => 'float')
214 f = CustomField.new(:field_format => 'float')
201
215
202 assert f.valid_field_value?(nil)
216 assert f.valid_field_value?(nil)
203 assert f.valid_field_value?('')
217 assert f.valid_field_value?('')
204 assert !f.valid_field_value?(' ')
218 assert !f.valid_field_value?(' ')
205 assert f.valid_field_value?('11.2')
219 assert f.valid_field_value?('11.2')
206 assert f.valid_field_value?('-6.250')
220 assert f.valid_field_value?('-6.250')
207 assert f.valid_field_value?('5')
221 assert f.valid_field_value?('5')
208 assert !f.valid_field_value?('6abc')
222 assert !f.valid_field_value?('6abc')
209 assert f.valid_field_value?(11.2)
223 assert f.valid_field_value?(11.2)
210 end
224 end
211
225
212 def test_multi_field_validation
226 def test_multi_field_validation
213 f = CustomField.new(:field_format => 'list', :multiple => 'true', :possible_values => ['value1', 'value2'])
227 f = CustomField.new(:field_format => 'list', :multiple => 'true', :possible_values => ['value1', 'value2'])
214
228
215 assert f.valid_field_value?(nil)
229 assert f.valid_field_value?(nil)
216 assert f.valid_field_value?('')
230 assert f.valid_field_value?('')
217 assert !f.valid_field_value?(' ')
231 assert !f.valid_field_value?(' ')
218 assert f.valid_field_value?([])
232 assert f.valid_field_value?([])
219 assert f.valid_field_value?([nil])
233 assert f.valid_field_value?([nil])
220 assert f.valid_field_value?([''])
234 assert f.valid_field_value?([''])
221 assert !f.valid_field_value?([' '])
235 assert !f.valid_field_value?([' '])
222
236
223 assert f.valid_field_value?('value2')
237 assert f.valid_field_value?('value2')
224 assert !f.valid_field_value?('abc')
238 assert !f.valid_field_value?('abc')
225
239
226 assert f.valid_field_value?(['value2'])
240 assert f.valid_field_value?(['value2'])
227 assert !f.valid_field_value?(['abc'])
241 assert !f.valid_field_value?(['abc'])
228
242
229 assert f.valid_field_value?(['', 'value2'])
243 assert f.valid_field_value?(['', 'value2'])
230 assert !f.valid_field_value?(['', 'abc'])
244 assert !f.valid_field_value?(['', 'abc'])
231
245
232 assert f.valid_field_value?(['value1', 'value2'])
246 assert f.valid_field_value?(['value1', 'value2'])
233 assert !f.valid_field_value?(['value1', 'abc'])
247 assert !f.valid_field_value?(['value1', 'abc'])
234 end
248 end
235
249
236 def test_changing_multiple_to_false_should_delete_multiple_values
250 def test_changing_multiple_to_false_should_delete_multiple_values
237 field = ProjectCustomField.create!(:name => 'field', :field_format => 'list', :multiple => 'true', :possible_values => ['field1', 'field2'])
251 field = ProjectCustomField.create!(:name => 'field', :field_format => 'list', :multiple => 'true', :possible_values => ['field1', 'field2'])
238 other = ProjectCustomField.create!(:name => 'other', :field_format => 'list', :multiple => 'true', :possible_values => ['other1', 'other2'])
252 other = ProjectCustomField.create!(:name => 'other', :field_format => 'list', :multiple => 'true', :possible_values => ['other1', 'other2'])
239
253
240 item_with_multiple_values = Project.generate!(:custom_field_values => {field.id => ['field1', 'field2'], other.id => ['other1', 'other2']})
254 item_with_multiple_values = Project.generate!(:custom_field_values => {field.id => ['field1', 'field2'], other.id => ['other1', 'other2']})
241 item_with_single_values = Project.generate!(:custom_field_values => {field.id => ['field1'], other.id => ['other2']})
255 item_with_single_values = Project.generate!(:custom_field_values => {field.id => ['field1'], other.id => ['other2']})
242
256
243 assert_difference 'CustomValue.count', -1 do
257 assert_difference 'CustomValue.count', -1 do
244 field.multiple = false
258 field.multiple = false
245 field.save!
259 field.save!
246 end
260 end
247
261
248 item_with_multiple_values = Project.find(item_with_multiple_values.id)
262 item_with_multiple_values = Project.find(item_with_multiple_values.id)
249 assert_kind_of String, item_with_multiple_values.custom_field_value(field)
263 assert_kind_of String, item_with_multiple_values.custom_field_value(field)
250 assert_kind_of Array, item_with_multiple_values.custom_field_value(other)
264 assert_kind_of Array, item_with_multiple_values.custom_field_value(other)
251 assert_equal 2, item_with_multiple_values.custom_field_value(other).size
265 assert_equal 2, item_with_multiple_values.custom_field_value(other).size
252 end
266 end
253
267
254 def test_value_class_should_return_the_class_used_for_fields_values
268 def test_value_class_should_return_the_class_used_for_fields_values
255 assert_equal User, CustomField.new(:field_format => 'user').value_class
269 assert_equal User, CustomField.new(:field_format => 'user').value_class
256 assert_equal Version, CustomField.new(:field_format => 'version').value_class
270 assert_equal Version, CustomField.new(:field_format => 'version').value_class
257 end
271 end
258
272
259 def test_value_class_should_return_nil_for_other_fields
273 def test_value_class_should_return_nil_for_other_fields
260 assert_nil CustomField.new(:field_format => 'text').value_class
274 assert_nil CustomField.new(:field_format => 'text').value_class
261 assert_nil CustomField.new.value_class
275 assert_nil CustomField.new.value_class
262 end
276 end
263
277
264 def test_value_from_keyword_for_list_custom_field
278 def test_value_from_keyword_for_list_custom_field
265 field = CustomField.find(1)
279 field = CustomField.find(1)
266 assert_equal 'PostgreSQL', field.value_from_keyword('postgresql', Issue.find(1))
280 assert_equal 'PostgreSQL', field.value_from_keyword('postgresql', Issue.find(1))
267 end
281 end
268
282
269 def test_visibile_scope_with_admin_should_return_all_custom_fields
283 def test_visibile_scope_with_admin_should_return_all_custom_fields
270 admin = User.generate! {|user| user.admin = true}
284 admin = User.generate! {|user| user.admin = true}
271 CustomField.delete_all
285 CustomField.delete_all
272 fields = [
286 fields = [
273 CustomField.generate!(:visible => true),
287 CustomField.generate!(:visible => true),
274 CustomField.generate!(:visible => false),
288 CustomField.generate!(:visible => false),
275 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
289 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
276 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
290 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
277 ]
291 ]
278
292
279 assert_equal 4, CustomField.visible(admin).count
293 assert_equal 4, CustomField.visible(admin).count
280 end
294 end
281
295
282 def test_visibile_scope_with_non_admin_user_should_return_visible_custom_fields
296 def test_visibile_scope_with_non_admin_user_should_return_visible_custom_fields
283 CustomField.delete_all
297 CustomField.delete_all
284 fields = [
298 fields = [
285 CustomField.generate!(:visible => true),
299 CustomField.generate!(:visible => true),
286 CustomField.generate!(:visible => false),
300 CustomField.generate!(:visible => false),
287 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
301 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
288 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
302 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
289 ]
303 ]
290 user = User.generate!
304 user = User.generate!
291 User.add_to_project(user, Project.first, Role.find(3))
305 User.add_to_project(user, Project.first, Role.find(3))
292
306
293 assert_equal [fields[0], fields[2]], CustomField.visible(user).order("id").to_a
307 assert_equal [fields[0], fields[2]], CustomField.visible(user).order("id").to_a
294 end
308 end
295
309
296 def test_visibile_scope_with_anonymous_user_should_return_visible_custom_fields
310 def test_visibile_scope_with_anonymous_user_should_return_visible_custom_fields
297 CustomField.delete_all
311 CustomField.delete_all
298 fields = [
312 fields = [
299 CustomField.generate!(:visible => true),
313 CustomField.generate!(:visible => true),
300 CustomField.generate!(:visible => false),
314 CustomField.generate!(:visible => false),
301 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
315 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
302 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
316 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
303 ]
317 ]
304
318
305 assert_equal [fields[0]], CustomField.visible(User.anonymous).order("id").to_a
319 assert_equal [fields[0]], CustomField.visible(User.anonymous).order("id").to_a
306 end
320 end
307
321
308 def test_float_cast_blank_value_should_return_nil
322 def test_float_cast_blank_value_should_return_nil
309 field = CustomField.new(:field_format => 'float')
323 field = CustomField.new(:field_format => 'float')
310 assert_equal nil, field.cast_value(nil)
324 assert_equal nil, field.cast_value(nil)
311 assert_equal nil, field.cast_value('')
325 assert_equal nil, field.cast_value('')
312 end
326 end
313
327
314 def test_float_cast_valid_value_should_return_float
328 def test_float_cast_valid_value_should_return_float
315 field = CustomField.new(:field_format => 'float')
329 field = CustomField.new(:field_format => 'float')
316 assert_equal 12.0, field.cast_value('12')
330 assert_equal 12.0, field.cast_value('12')
317 assert_equal 12.5, field.cast_value('12.5')
331 assert_equal 12.5, field.cast_value('12.5')
318 assert_equal 12.5, field.cast_value('+12.5')
332 assert_equal 12.5, field.cast_value('+12.5')
319 assert_equal -12.5, field.cast_value('-12.5')
333 assert_equal -12.5, field.cast_value('-12.5')
320 end
334 end
321 end
335 end
General Comments 0
You need to be logged in to leave comments. Login now