##// END OF EJS Templates
Prevents no method errors when reloading in development mode....
Jean-Philippe Lang -
r12396:caa7af66e667
parent child
Show More
@@ -1,281 +1,283
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class CustomField < ActiveRecord::Base
19 19 include Redmine::SubclassFactory
20 20
21 21 has_many :custom_values, :dependent => :delete_all
22 22 has_and_belongs_to_many :roles, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "custom_field_id"
23 23 acts_as_list :scope => 'type = \'#{self.class}\''
24 24 serialize :possible_values
25 25 store :format_store
26 26
27 27 validates_presence_of :name, :field_format
28 28 validates_uniqueness_of :name, :scope => :type
29 29 validates_length_of :name, :maximum => 30
30 30 validates_inclusion_of :field_format, :in => Proc.new { Redmine::FieldFormat.available_formats }
31 31 validate :validate_custom_field
32 32
33 33 before_validation :set_searchable
34 34 before_save do |field|
35 35 field.format.before_custom_field_save(field)
36 36 end
37 37 after_save :handle_multiplicity_change
38 38 after_save do |field|
39 39 if field.visible_changed? && field.visible
40 40 field.roles.clear
41 41 end
42 42 end
43 43
44 44 scope :sorted, lambda { order("#{table_name}.position ASC") }
45 45 scope :visible, lambda {|*args|
46 46 user = args.shift || User.current
47 47 if user.admin?
48 48 # nop
49 49 elsif user.memberships.any?
50 50 where("#{table_name}.visible = ? OR #{table_name}.id IN (SELECT DISTINCT cfr.custom_field_id FROM #{Member.table_name} m" +
51 51 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
52 52 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
53 53 " WHERE m.user_id = ?)",
54 54 true, user.id)
55 55 else
56 56 where(:visible => true)
57 57 end
58 58 }
59 59
60 60 def visible_by?(project, user=User.current)
61 61 visible? || user.admin?
62 62 end
63 63
64 64 def format
65 65 @format ||= Redmine::FieldFormat.find(field_format)
66 66 end
67 67
68 68 def field_format=(arg)
69 69 # cannot change format of a saved custom field
70 70 if new_record?
71 71 @format = nil
72 72 super
73 73 end
74 74 end
75 75
76 76 def set_searchable
77 77 # make sure these fields are not searchable
78 78 self.searchable = false unless format.class.searchable_supported
79 79 # make sure only these fields can have multiple values
80 80 self.multiple = false unless format.class.multiple_supported
81 81 true
82 82 end
83 83
84 84 def validate_custom_field
85 85 format.validate_custom_field(self).each do |attribute, message|
86 86 errors.add attribute, message
87 87 end
88 88
89 89 if regexp.present?
90 90 begin
91 91 Regexp.new(regexp)
92 92 rescue
93 93 errors.add(:regexp, :invalid)
94 94 end
95 95 end
96 96
97 97 if default_value.present?
98 98 validate_field_value(default_value).each do |message|
99 99 errors.add :default_value, message
100 100 end
101 101 end
102 102 end
103 103
104 104 def possible_custom_value_options(custom_value)
105 105 format.possible_custom_value_options(custom_value)
106 106 end
107 107
108 108 def possible_values_options(object=nil)
109 109 if object.is_a?(Array)
110 110 object.map {|o| format.possible_values_options(self, o)}.reduce(:&) || []
111 111 else
112 112 format.possible_values_options(self, object) || []
113 113 end
114 114 end
115 115
116 116 def possible_values
117 117 values = super()
118 118 if values.is_a?(Array)
119 119 values.each do |value|
120 120 value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
121 121 end
122 122 values
123 123 else
124 124 []
125 125 end
126 126 end
127 127
128 128 # Makes possible_values accept a multiline string
129 129 def possible_values=(arg)
130 130 if arg.is_a?(Array)
131 131 super(arg.compact.collect(&:strip).select {|v| !v.blank?})
132 132 else
133 133 self.possible_values = arg.to_s.split(/[\n\r]+/)
134 134 end
135 135 end
136 136
137 137 def cast_value(value)
138 138 format.cast_value(self, value)
139 139 end
140 140
141 141 def value_from_keyword(keyword, customized)
142 142 possible_values_options = possible_values_options(customized)
143 143 if possible_values_options.present?
144 144 keyword = keyword.to_s.downcase
145 145 if v = possible_values_options.detect {|text, id| text.downcase == keyword}
146 146 if v.is_a?(Array)
147 147 v.last
148 148 else
149 149 v
150 150 end
151 151 end
152 152 else
153 153 keyword
154 154 end
155 155 end
156 156
157 157 # Returns a ORDER BY clause that can used to sort customized
158 158 # objects by their value of the custom field.
159 159 # Returns nil if the custom field can not be used for sorting.
160 160 def order_statement
161 161 return nil if multiple?
162 162 format.order_statement(self)
163 163 end
164 164
165 165 # Returns a GROUP BY clause that can used to group by custom value
166 166 # Returns nil if the custom field can not be used for grouping.
167 167 def group_statement
168 168 return nil if multiple?
169 169 format.group_statement(self)
170 170 end
171 171
172 172 def join_for_order_statement
173 173 format.join_for_order_statement(self)
174 174 end
175 175
176 176 def visibility_by_project_condition(project_key=nil, user=User.current, id_column=nil)
177 177 if visible? || user.admin?
178 178 "1=1"
179 179 elsif user.anonymous?
180 180 "1=0"
181 181 else
182 182 project_key ||= "#{self.class.customized_class.table_name}.project_id"
183 183 id_column ||= id
184 184 "#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
185 185 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
186 186 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
187 187 " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id_column})"
188 188 end
189 189 end
190 190
191 191 def self.visibility_condition
192 192 if user.admin?
193 193 "1=1"
194 194 elsif user.anonymous?
195 195 "#{table_name}.visible"
196 196 else
197 197 "#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
198 198 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
199 199 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
200 200 " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id})"
201 201 end
202 202 end
203 203
204 204 def <=>(field)
205 205 position <=> field.position
206 206 end
207 207
208 208 # Returns the class that values represent
209 209 def value_class
210 210 format.target_class if format.respond_to?(:target_class)
211 211 end
212 212
213 213 def self.customized_class
214 214 self.name =~ /^(.+)CustomField$/
215 215 $1.constantize rescue nil
216 216 end
217 217
218 218 # to move in project_custom_field
219 219 def self.for_all
220 220 where(:is_for_all => true).order('position').all
221 221 end
222 222
223 223 def type_name
224 224 nil
225 225 end
226 226
227 227 # Returns the error messages for the given value
228 228 # or an empty array if value is a valid value for the custom field
229 229 def validate_custom_value(custom_value)
230 230 value = custom_value.value
231 231 errs = []
232 232 if value.is_a?(Array)
233 233 if !multiple?
234 234 errs << ::I18n.t('activerecord.errors.messages.invalid')
235 235 end
236 236 if is_required? && value.detect(&:present?).nil?
237 237 errs << ::I18n.t('activerecord.errors.messages.blank')
238 238 end
239 239 else
240 240 if is_required? && value.blank?
241 241 errs << ::I18n.t('activerecord.errors.messages.blank')
242 242 end
243 243 end
244 244 if custom_value.value.present?
245 245 errs += format.validate_custom_value(custom_value)
246 246 end
247 247 errs
248 248 end
249 249
250 250 # Returns the error messages for the default custom field value
251 251 def validate_field_value(value)
252 252 validate_custom_value(CustomValue.new(:custom_field => self, :value => value))
253 253 end
254 254
255 255 # Returns true if value is a valid value for the custom field
256 256 def valid_field_value?(value)
257 257 validate_field_value(value).empty?
258 258 end
259 259
260 260 def format_in?(*args)
261 261 args.include?(field_format)
262 262 end
263 263
264 264 protected
265 265
266 266 # Removes multiple values for the custom field after setting the multiple attribute to false
267 267 # We kepp the value with the highest id for each customized object
268 268 def handle_multiplicity_change
269 269 if !new_record? && multiple_was && !multiple
270 270 ids = custom_values.
271 271 where("EXISTS(SELECT 1 FROM #{CustomValue.table_name} cve WHERE cve.custom_field_id = #{CustomValue.table_name}.custom_field_id" +
272 272 " AND cve.customized_type = #{CustomValue.table_name}.customized_type AND cve.customized_id = #{CustomValue.table_name}.customized_id" +
273 273 " AND cve.id > #{CustomValue.table_name}.id)").
274 274 pluck(:id)
275 275
276 276 if ids.any?
277 277 custom_values.where(:id => ids).delete_all
278 278 end
279 279 end
280 280 end
281 281 end
282
283 require_dependency 'redmine/field_format'
General Comments 0
You need to be logged in to leave comments. Login now