##// END OF EJS Templates
Fixes #possible_values for version custom field....
Jean-Philippe Lang -
r5233:406aa946e52d
parent child
Show More
@@ -1,149 +1,149
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 has_many :custom_values, :dependent => :delete_all
19 has_many :custom_values, :dependent => :delete_all
20 acts_as_list :scope => 'type = \'#{self.class}\''
20 acts_as_list :scope => 'type = \'#{self.class}\''
21 serialize :possible_values
21 serialize :possible_values
22
22
23 validates_presence_of :name, :field_format
23 validates_presence_of :name, :field_format
24 validates_uniqueness_of :name, :scope => :type
24 validates_uniqueness_of :name, :scope => :type
25 validates_length_of :name, :maximum => 30
25 validates_length_of :name, :maximum => 30
26 validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
26 validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
27
27
28 def initialize(attributes = nil)
28 def initialize(attributes = nil)
29 super
29 super
30 self.possible_values ||= []
30 self.possible_values ||= []
31 end
31 end
32
32
33 def before_validation
33 def before_validation
34 # make sure these fields are not searchable
34 # make sure these fields are not searchable
35 self.searchable = false if %w(int float date bool).include?(field_format)
35 self.searchable = false if %w(int float date bool).include?(field_format)
36 true
36 true
37 end
37 end
38
38
39 def validate
39 def validate
40 if self.field_format == "list"
40 if self.field_format == "list"
41 errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty?
41 errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty?
42 errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
42 errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
43 end
43 end
44
44
45 # validate default value
45 # validate default value
46 v = CustomValue.new(:custom_field => self.clone, :value => default_value, :customized => nil)
46 v = CustomValue.new(:custom_field => self.clone, :value => default_value, :customized => nil)
47 v.custom_field.is_required = false
47 v.custom_field.is_required = false
48 errors.add(:default_value, :invalid) unless v.valid?
48 errors.add(:default_value, :invalid) unless v.valid?
49 end
49 end
50
50
51 def possible_values_options(obj=nil)
51 def possible_values_options(obj=nil)
52 case field_format
52 case field_format
53 when 'user', 'version'
53 when 'user', 'version'
54 if obj.respond_to?(:project) && obj.project
54 if obj.respond_to?(:project) && obj.project
55 case field_format
55 case field_format
56 when 'user'
56 when 'user'
57 obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
57 obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
58 when 'version'
58 when 'version'
59 obj.project.versions.sort.collect {|u| [u.to_s, u.id.to_s]}
59 obj.project.versions.sort.collect {|u| [u.to_s, u.id.to_s]}
60 end
60 end
61 else
61 else
62 []
62 []
63 end
63 end
64 else
64 else
65 read_attribute :possible_values
65 read_attribute :possible_values
66 end
66 end
67 end
67 end
68
68
69 def possible_values(obj=nil)
69 def possible_values(obj=nil)
70 case field_format
70 case field_format
71 when 'user'
71 when 'user', 'version'
72 possible_values_options(obj).collect(&:last)
72 possible_values_options(obj).collect(&:last)
73 else
73 else
74 read_attribute :possible_values
74 read_attribute :possible_values
75 end
75 end
76 end
76 end
77
77
78 # Makes possible_values accept a multiline string
78 # Makes possible_values accept a multiline string
79 def possible_values=(arg)
79 def possible_values=(arg)
80 if arg.is_a?(Array)
80 if arg.is_a?(Array)
81 write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
81 write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
82 else
82 else
83 self.possible_values = arg.to_s.split(/[\n\r]+/)
83 self.possible_values = arg.to_s.split(/[\n\r]+/)
84 end
84 end
85 end
85 end
86
86
87 def cast_value(value)
87 def cast_value(value)
88 casted = nil
88 casted = nil
89 unless value.blank?
89 unless value.blank?
90 case field_format
90 case field_format
91 when 'string', 'text', 'list'
91 when 'string', 'text', 'list'
92 casted = value
92 casted = value
93 when 'date'
93 when 'date'
94 casted = begin; value.to_date; rescue; nil end
94 casted = begin; value.to_date; rescue; nil end
95 when 'bool'
95 when 'bool'
96 casted = (value == '1' ? true : false)
96 casted = (value == '1' ? true : false)
97 when 'int'
97 when 'int'
98 casted = value.to_i
98 casted = value.to_i
99 when 'float'
99 when 'float'
100 casted = value.to_f
100 casted = value.to_f
101 when 'user', 'version'
101 when 'user', 'version'
102 casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
102 casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
103 end
103 end
104 end
104 end
105 casted
105 casted
106 end
106 end
107
107
108 # Returns a ORDER BY clause that can used to sort customized
108 # Returns a ORDER BY clause that can used to sort customized
109 # objects by their value of the custom field.
109 # objects by their value of the custom field.
110 # Returns false, if the custom field can not be used for sorting.
110 # Returns false, if the custom field can not be used for sorting.
111 def order_statement
111 def order_statement
112 case field_format
112 case field_format
113 when 'string', 'text', 'list', 'date', 'bool'
113 when 'string', 'text', 'list', 'date', 'bool'
114 # COALESCE is here to make sure that blank and NULL values are sorted equally
114 # COALESCE is here to make sure that blank and NULL values are sorted equally
115 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
115 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
116 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
116 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
117 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
117 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
118 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
118 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
119 when 'int', 'float'
119 when 'int', 'float'
120 # Make the database cast values into numeric
120 # Make the database cast values into numeric
121 # Postgresql will raise an error if a value can not be casted!
121 # Postgresql will raise an error if a value can not be casted!
122 # CustomValue validations should ensure that it doesn't occur
122 # CustomValue validations should ensure that it doesn't occur
123 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
123 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
124 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
124 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
125 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
125 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
126 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
126 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
127 else
127 else
128 nil
128 nil
129 end
129 end
130 end
130 end
131
131
132 def <=>(field)
132 def <=>(field)
133 position <=> field.position
133 position <=> field.position
134 end
134 end
135
135
136 def self.customized_class
136 def self.customized_class
137 self.name =~ /^(.+)CustomField$/
137 self.name =~ /^(.+)CustomField$/
138 begin; $1.constantize; rescue nil; end
138 begin; $1.constantize; rescue nil; end
139 end
139 end
140
140
141 # to move in project_custom_field
141 # to move in project_custom_field
142 def self.for_all
142 def self.for_all
143 find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
143 find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
144 end
144 end
145
145
146 def type_name
146 def type_name
147 nil
147 nil
148 end
148 end
149 end
149 end
General Comments 0
You need to be logged in to leave comments. Login now