comparison app/models/custom_field.rb @ 1295:622f24f53b42 redmine-2.3

Update to Redmine SVN revision 11972 on 2.3-stable branch
author Chris Cannam
date Fri, 14 Jun 2013 09:02:21 +0100
parents 433d4f72a19b
children
comparison
equal deleted inserted replaced
1294:3e4c3460b6ca 1295:622f24f53b42
1 # Redmine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang 2 # Copyright (C) 2006-2013 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.
27 validates_length_of :name, :maximum => 30 27 validates_length_of :name, :maximum => 30
28 validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats 28 validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
29 29
30 validate :validate_custom_field 30 validate :validate_custom_field
31 before_validation :set_searchable 31 before_validation :set_searchable
32 after_save :handle_multiplicity_change
33
34 scope :sorted, lambda { order("#{table_name}.position ASC") }
32 35
33 CUSTOM_FIELDS_TABS = [ 36 CUSTOM_FIELDS_TABS = [
34 {:name => 'IssueCustomField', :partial => 'custom_fields/index', 37 {:name => 'IssueCustomField', :partial => 'custom_fields/index',
35 :label => :label_issue_plural}, 38 :label => :label_issue_plural},
36 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', 39 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index',
167 end 170 end
168 else 171 else
169 keyword 172 keyword
170 end 173 end
171 end 174 end
172 175
173 # Returns a ORDER BY clause that can used to sort customized 176 # Returns a ORDER BY clause that can used to sort customized
174 # objects by their value of the custom field. 177 # objects by their value of the custom field.
175 # Returns nil if the custom field can not be used for sorting. 178 # Returns nil if the custom field can not be used for sorting.
176 def order_statement 179 def order_statement
177 return nil if multiple? 180 return nil if multiple?
178 case field_format 181 case field_format
179 when 'string', 'text', 'list', 'date', 'bool' 182 when 'string', 'text', 'list', 'date', 'bool'
180 # COALESCE is here to make sure that blank and NULL values are sorted equally 183 # COALESCE is here to make sure that blank and NULL values are sorted equally
181 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" + 184 "COALESCE(#{join_alias}.value, '')"
182 " WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
183 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
184 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
185 when 'int', 'float' 185 when 'int', 'float'
186 # Make the database cast values into numeric 186 # Make the database cast values into numeric
187 # Postgresql will raise an error if a value can not be casted! 187 # Postgresql will raise an error if a value can not be casted!
188 # CustomValue validations should ensure that it doesn't occur 188 # CustomValue validations should ensure that it doesn't occur
189 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" + 189 "CAST(CASE #{join_alias}.value WHEN '' THEN '0' ELSE #{join_alias}.value END AS decimal(30,3))"
190 " WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
191 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
192 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
193 when 'user', 'version' 190 when 'user', 'version'
194 value_class.fields_for_order_statement(value_join_alias) 191 value_class.fields_for_order_statement(value_join_alias)
195 else 192 else
196 nil 193 nil
197 end 194 end
198 end 195 end
199 196
200 # Returns a GROUP BY clause that can used to group by custom value 197 # Returns a GROUP BY clause that can used to group by custom value
201 # Returns nil if the custom field can not be used for grouping. 198 # Returns nil if the custom field can not be used for grouping.
202 def group_statement 199 def group_statement
203 return nil if multiple? 200 return nil if multiple?
204 case field_format 201 case field_format
205 when 'list', 'date', 'bool', 'int' 202 when 'list', 'date', 'bool', 'int'
206 order_statement 203 order_statement
207 when 'user', 'version' 204 when 'user', 'version'
208 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" + 205 "COALESCE(#{join_alias}.value, '')"
209 " WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
210 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
211 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
212 else 206 else
213 nil 207 nil
214 end 208 end
215 end 209 end
216 210
225 " AND #{join_alias}.id = (SELECT max(#{join_alias}_2.id) FROM #{CustomValue.table_name} #{join_alias}_2" + 219 " AND #{join_alias}.id = (SELECT max(#{join_alias}_2.id) FROM #{CustomValue.table_name} #{join_alias}_2" +
226 " WHERE #{join_alias}_2.customized_type = #{join_alias}.customized_type" + 220 " WHERE #{join_alias}_2.customized_type = #{join_alias}.customized_type" +
227 " AND #{join_alias}_2.customized_id = #{join_alias}.customized_id" + 221 " AND #{join_alias}_2.customized_id = #{join_alias}.customized_id" +
228 " AND #{join_alias}_2.custom_field_id = #{join_alias}.custom_field_id)" + 222 " AND #{join_alias}_2.custom_field_id = #{join_alias}.custom_field_id)" +
229 " LEFT OUTER JOIN #{value_class.table_name} #{value_join_alias}" + 223 " LEFT OUTER JOIN #{value_class.table_name} #{value_join_alias}" +
230 " ON CAST(#{join_alias}.value as decimal(60,0)) = #{value_join_alias}.id" 224 " ON CAST(CASE #{join_alias}.value WHEN '' THEN '0' ELSE #{join_alias}.value END AS decimal(30,0)) = #{value_join_alias}.id"
225 when 'int', 'float'
226 "LEFT OUTER JOIN #{CustomValue.table_name} #{join_alias}" +
227 " ON #{join_alias}.customized_type = '#{self.class.customized_class.base_class.name}'" +
228 " AND #{join_alias}.customized_id = #{self.class.customized_class.table_name}.id" +
229 " AND #{join_alias}.custom_field_id = #{id}" +
230 " AND #{join_alias}.value <> ''" +
231 " AND #{join_alias}.id = (SELECT max(#{join_alias}_2.id) FROM #{CustomValue.table_name} #{join_alias}_2" +
232 " WHERE #{join_alias}_2.customized_type = #{join_alias}.customized_type" +
233 " AND #{join_alias}_2.customized_id = #{join_alias}.customized_id" +
234 " AND #{join_alias}_2.custom_field_id = #{join_alias}.custom_field_id)"
235 when 'string', 'text', 'list', 'date', 'bool'
236 "LEFT OUTER JOIN #{CustomValue.table_name} #{join_alias}" +
237 " ON #{join_alias}.customized_type = '#{self.class.customized_class.base_class.name}'" +
238 " AND #{join_alias}.customized_id = #{self.class.customized_class.table_name}.id" +
239 " AND #{join_alias}.custom_field_id = #{id}" +
240 " AND #{join_alias}.id = (SELECT max(#{join_alias}_2.id) FROM #{CustomValue.table_name} #{join_alias}_2" +
241 " WHERE #{join_alias}_2.customized_type = #{join_alias}.customized_type" +
242 " AND #{join_alias}_2.customized_id = #{join_alias}.customized_id" +
243 " AND #{join_alias}_2.custom_field_id = #{join_alias}.custom_field_id)"
231 else 244 else
232 nil 245 nil
233 end 246 end
234 end 247 end
235 248
260 begin; $1.constantize; rescue nil; end 273 begin; $1.constantize; rescue nil; end
261 end 274 end
262 275
263 # to move in project_custom_field 276 # to move in project_custom_field
264 def self.for_all 277 def self.for_all
265 find(:all, :conditions => ["is_for_all=?", true], :order => 'position') 278 where(:is_for_all => true).order('position').all
266 end 279 end
267 280
268 def type_name 281 def type_name
269 nil 282 nil
270 end 283 end
321 errs << ::I18n.t('activerecord.errors.messages.inclusion') unless possible_values.include?(value) 334 errs << ::I18n.t('activerecord.errors.messages.inclusion') unless possible_values.include?(value)
322 end 335 end
323 end 336 end
324 errs 337 errs
325 end 338 end
339
340 # Removes multiple values for the custom field after setting the multiple attribute to false
341 # We kepp the value with the highest id for each customized object
342 def handle_multiplicity_change
343 if !new_record? && multiple_was && !multiple
344 ids = custom_values.
345 where("EXISTS(SELECT 1 FROM #{CustomValue.table_name} cve WHERE cve.custom_field_id = #{CustomValue.table_name}.custom_field_id" +
346 " AND cve.customized_type = #{CustomValue.table_name}.customized_type AND cve.customized_id = #{CustomValue.table_name}.customized_id" +
347 " AND cve.id > #{CustomValue.table_name}.id)").
348 pluck(:id)
349
350 if ids.any?
351 custom_values.where(:id => ids).delete_all
352 end
353 end
354 end
326 end 355 end