##// END OF EJS Templates
Merged r16134 (#24283)....
Jean-Philippe Lang -
r15753:01e985eb0ada
parent child
Show More
@@ -1,293 +1,293
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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 :enumerations,
22 22 lambda { order(:position) },
23 23 :class_name => 'CustomFieldEnumeration',
24 24 :dependent => :delete_all
25 25 has_many :custom_values, :dependent => :delete_all
26 26 has_and_belongs_to_many :roles, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "custom_field_id"
27 27 acts_as_positioned
28 28 serialize :possible_values
29 29 store :format_store
30 30
31 31 validates_presence_of :name, :field_format
32 32 validates_uniqueness_of :name, :scope => :type
33 33 validates_length_of :name, :maximum => 30
34 validates_length_of :regexp, maximum: 30
34 validates_length_of :regexp, maximum: 255
35 35 validates_inclusion_of :field_format, :in => Proc.new { Redmine::FieldFormat.available_formats }
36 36 validate :validate_custom_field
37 37 attr_protected :id
38 38
39 39 before_validation :set_searchable
40 40 before_save do |field|
41 41 field.format.before_custom_field_save(field)
42 42 end
43 43 after_save :handle_multiplicity_change
44 44 after_save do |field|
45 45 if field.visible_changed? && field.visible
46 46 field.roles.clear
47 47 end
48 48 end
49 49
50 50 scope :sorted, lambda { order(:position) }
51 51 scope :visible, lambda {|*args|
52 52 user = args.shift || User.current
53 53 if user.admin?
54 54 # nop
55 55 elsif user.memberships.any?
56 56 where("#{table_name}.visible = ? OR #{table_name}.id IN (SELECT DISTINCT cfr.custom_field_id FROM #{Member.table_name} m" +
57 57 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
58 58 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
59 59 " WHERE m.user_id = ?)",
60 60 true, user.id)
61 61 else
62 62 where(:visible => true)
63 63 end
64 64 }
65 65
66 66 def visible_by?(project, user=User.current)
67 67 visible? || user.admin?
68 68 end
69 69
70 70 def format
71 71 @format ||= Redmine::FieldFormat.find(field_format)
72 72 end
73 73
74 74 def field_format=(arg)
75 75 # cannot change format of a saved custom field
76 76 if new_record?
77 77 @format = nil
78 78 super
79 79 end
80 80 end
81 81
82 82 def set_searchable
83 83 # make sure these fields are not searchable
84 84 self.searchable = false unless format.class.searchable_supported
85 85 # make sure only these fields can have multiple values
86 86 self.multiple = false unless format.class.multiple_supported
87 87 true
88 88 end
89 89
90 90 def validate_custom_field
91 91 format.validate_custom_field(self).each do |attribute, message|
92 92 errors.add attribute, message
93 93 end
94 94
95 95 if regexp.present?
96 96 begin
97 97 Regexp.new(regexp)
98 98 rescue
99 99 errors.add(:regexp, :invalid)
100 100 end
101 101 end
102 102
103 103 if default_value.present?
104 104 validate_field_value(default_value).each do |message|
105 105 errors.add :default_value, message
106 106 end
107 107 end
108 108 end
109 109
110 110 def possible_custom_value_options(custom_value)
111 111 format.possible_custom_value_options(custom_value)
112 112 end
113 113
114 114 def possible_values_options(object=nil)
115 115 if object.is_a?(Array)
116 116 object.map {|o| format.possible_values_options(self, o)}.reduce(:&) || []
117 117 else
118 118 format.possible_values_options(self, object) || []
119 119 end
120 120 end
121 121
122 122 def possible_values
123 123 values = read_attribute(:possible_values)
124 124 if values.is_a?(Array)
125 125 values.each do |value|
126 126 value.to_s.force_encoding('UTF-8')
127 127 end
128 128 values
129 129 else
130 130 []
131 131 end
132 132 end
133 133
134 134 # Makes possible_values accept a multiline string
135 135 def possible_values=(arg)
136 136 if arg.is_a?(Array)
137 137 values = arg.compact.map {|a| a.to_s.strip}.reject(&:blank?)
138 138 write_attribute(:possible_values, values)
139 139 else
140 140 self.possible_values = arg.to_s.split(/[\n\r]+/)
141 141 end
142 142 end
143 143
144 144 def cast_value(value)
145 145 format.cast_value(self, value)
146 146 end
147 147
148 148 def value_from_keyword(keyword, customized)
149 149 format.value_from_keyword(self, keyword, customized)
150 150 end
151 151
152 152 # Returns the options hash used to build a query filter for the field
153 153 def query_filter_options(query)
154 154 format.query_filter_options(self, query)
155 155 end
156 156
157 157 def totalable?
158 158 format.totalable_supported
159 159 end
160 160
161 161 # Returns a ORDER BY clause that can used to sort customized
162 162 # objects by their value of the custom field.
163 163 # Returns nil if the custom field can not be used for sorting.
164 164 def order_statement
165 165 return nil if multiple?
166 166 format.order_statement(self)
167 167 end
168 168
169 169 # Returns a GROUP BY clause that can used to group by custom value
170 170 # Returns nil if the custom field can not be used for grouping.
171 171 def group_statement
172 172 return nil if multiple?
173 173 format.group_statement(self)
174 174 end
175 175
176 176 def join_for_order_statement
177 177 format.join_for_order_statement(self)
178 178 end
179 179
180 180 def visibility_by_project_condition(project_key=nil, user=User.current, id_column=nil)
181 181 if visible? || user.admin?
182 182 "1=1"
183 183 elsif user.anonymous?
184 184 "1=0"
185 185 else
186 186 project_key ||= "#{self.class.customized_class.table_name}.project_id"
187 187 id_column ||= id
188 188 "#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
189 189 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
190 190 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
191 191 " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id_column})"
192 192 end
193 193 end
194 194
195 195 def self.visibility_condition
196 196 if user.admin?
197 197 "1=1"
198 198 elsif user.anonymous?
199 199 "#{table_name}.visible"
200 200 else
201 201 "#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" +
202 202 " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" +
203 203 " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" +
204 204 " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id})"
205 205 end
206 206 end
207 207
208 208 def <=>(field)
209 209 position <=> field.position
210 210 end
211 211
212 212 # Returns the class that values represent
213 213 def value_class
214 214 format.target_class if format.respond_to?(:target_class)
215 215 end
216 216
217 217 def self.customized_class
218 218 self.name =~ /^(.+)CustomField$/
219 219 $1.constantize rescue nil
220 220 end
221 221
222 222 # to move in project_custom_field
223 223 def self.for_all
224 224 where(:is_for_all => true).order('position').to_a
225 225 end
226 226
227 227 def type_name
228 228 nil
229 229 end
230 230
231 231 # Returns the error messages for the given value
232 232 # or an empty array if value is a valid value for the custom field
233 233 def validate_custom_value(custom_value)
234 234 value = custom_value.value
235 235 errs = []
236 236 if value.is_a?(Array)
237 237 if !multiple?
238 238 errs << ::I18n.t('activerecord.errors.messages.invalid')
239 239 end
240 240 if is_required? && value.detect(&:present?).nil?
241 241 errs << ::I18n.t('activerecord.errors.messages.blank')
242 242 end
243 243 else
244 244 if is_required? && value.blank?
245 245 errs << ::I18n.t('activerecord.errors.messages.blank')
246 246 end
247 247 end
248 248 errs += format.validate_custom_value(custom_value)
249 249 errs
250 250 end
251 251
252 252 # Returns the error messages for the default custom field value
253 253 def validate_field_value(value)
254 254 validate_custom_value(CustomFieldValue.new(:custom_field => self, :value => value))
255 255 end
256 256
257 257 # Returns true if value is a valid value for the custom field
258 258 def valid_field_value?(value)
259 259 validate_field_value(value).empty?
260 260 end
261 261
262 262 def format_in?(*args)
263 263 args.include?(field_format)
264 264 end
265 265
266 266 def self.human_attribute_name(attribute_key_name, *args)
267 267 attr_name = attribute_key_name.to_s
268 268 if attr_name == 'url_pattern'
269 269 attr_name = "url"
270 270 end
271 271 super(attr_name, *args)
272 272 end
273 273
274 274 protected
275 275
276 276 # Removes multiple values for the custom field after setting the multiple attribute to false
277 277 # We kepp the value with the highest id for each customized object
278 278 def handle_multiplicity_change
279 279 if !new_record? && multiple_was && !multiple
280 280 ids = custom_values.
281 281 where("EXISTS(SELECT 1 FROM #{CustomValue.table_name} cve WHERE cve.custom_field_id = #{CustomValue.table_name}.custom_field_id" +
282 282 " AND cve.customized_type = #{CustomValue.table_name}.customized_type AND cve.customized_id = #{CustomValue.table_name}.customized_id" +
283 283 " AND cve.id > #{CustomValue.table_name}.id)").
284 284 pluck(:id)
285 285
286 286 if ids.any?
287 287 custom_values.where(:id => ids).delete_all
288 288 end
289 289 end
290 290 end
291 291 end
292 292
293 293 require_dependency 'redmine/field_format'
General Comments 0
You need to be logged in to leave comments. Login now