##// END OF EJS Templates
move CustomFieldsHelper tabs variable to model constant for common use (#12018)...
Toshi MARUYAMA -
r10343:fb4210b6d969
parent child
Show More
@@ -1,158 +1,149
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2012 Jean-Philippe Lang
4 # Copyright (C) 2006-2012 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 module CustomFieldsHelper
20 module CustomFieldsHelper
21
21
22 def custom_fields_tabs
22 def custom_fields_tabs
23 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
23 CustomField::CUSTOM_FIELDS_TABS
24 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
25 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
26 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
27 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
28 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
29 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
30 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
31 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
32 ]
33 end
24 end
34
25
35 # Return custom field html tag corresponding to its format
26 # Return custom field html tag corresponding to its format
36 def custom_field_tag(name, custom_value)
27 def custom_field_tag(name, custom_value)
37 custom_field = custom_value.custom_field
28 custom_field = custom_value.custom_field
38 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
29 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
39 field_name << "[]" if custom_field.multiple?
30 field_name << "[]" if custom_field.multiple?
40 field_id = "#{name}_custom_field_values_#{custom_field.id}"
31 field_id = "#{name}_custom_field_values_#{custom_field.id}"
41
32
42 tag_options = {:id => field_id, :class => "#{custom_field.field_format}_cf"}
33 tag_options = {:id => field_id, :class => "#{custom_field.field_format}_cf"}
43
34
44 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
35 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
45 case field_format.try(:edit_as)
36 case field_format.try(:edit_as)
46 when "date"
37 when "date"
47 text_field_tag(field_name, custom_value.value, tag_options.merge(:size => 10)) +
38 text_field_tag(field_name, custom_value.value, tag_options.merge(:size => 10)) +
48 calendar_for(field_id)
39 calendar_for(field_id)
49 when "text"
40 when "text"
50 text_area_tag(field_name, custom_value.value, tag_options.merge(:rows => 3))
41 text_area_tag(field_name, custom_value.value, tag_options.merge(:rows => 3))
51 when "bool"
42 when "bool"
52 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, tag_options)
43 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, tag_options)
53 when "list"
44 when "list"
54 blank_option = ''.html_safe
45 blank_option = ''.html_safe
55 unless custom_field.multiple?
46 unless custom_field.multiple?
56 if custom_field.is_required?
47 if custom_field.is_required?
57 unless custom_field.default_value.present?
48 unless custom_field.default_value.present?
58 blank_option = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---", :value => '')
49 blank_option = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---", :value => '')
59 end
50 end
60 else
51 else
61 blank_option = content_tag('option')
52 blank_option = content_tag('option')
62 end
53 end
63 end
54 end
64 s = select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
55 s = select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
65 tag_options.merge(:multiple => custom_field.multiple?))
56 tag_options.merge(:multiple => custom_field.multiple?))
66 if custom_field.multiple?
57 if custom_field.multiple?
67 s << hidden_field_tag(field_name, '')
58 s << hidden_field_tag(field_name, '')
68 end
59 end
69 s
60 s
70 else
61 else
71 text_field_tag(field_name, custom_value.value, tag_options)
62 text_field_tag(field_name, custom_value.value, tag_options)
72 end
63 end
73 end
64 end
74
65
75 # Return custom field label tag
66 # Return custom field label tag
76 def custom_field_label_tag(name, custom_value, options={})
67 def custom_field_label_tag(name, custom_value, options={})
77 required = options[:required] || custom_value.custom_field.is_required?
68 required = options[:required] || custom_value.custom_field.is_required?
78
69
79 content_tag "label", h(custom_value.custom_field.name) +
70 content_tag "label", h(custom_value.custom_field.name) +
80 (required ? " <span class=\"required\">*</span>".html_safe : ""),
71 (required ? " <span class=\"required\">*</span>".html_safe : ""),
81 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
72 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
82 end
73 end
83
74
84 # Return custom field tag with its label tag
75 # Return custom field tag with its label tag
85 def custom_field_tag_with_label(name, custom_value, options={})
76 def custom_field_tag_with_label(name, custom_value, options={})
86 custom_field_label_tag(name, custom_value, options) + custom_field_tag(name, custom_value)
77 custom_field_label_tag(name, custom_value, options) + custom_field_tag(name, custom_value)
87 end
78 end
88
79
89 def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
80 def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
90 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
81 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
91 field_name << "[]" if custom_field.multiple?
82 field_name << "[]" if custom_field.multiple?
92 field_id = "#{name}_custom_field_values_#{custom_field.id}"
83 field_id = "#{name}_custom_field_values_#{custom_field.id}"
93
84
94 tag_options = {:id => field_id, :class => "#{custom_field.field_format}_cf"}
85 tag_options = {:id => field_id, :class => "#{custom_field.field_format}_cf"}
95
86
96 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
87 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
97 case field_format.try(:edit_as)
88 case field_format.try(:edit_as)
98 when "date"
89 when "date"
99 text_field_tag(field_name, '', tag_options.merge(:size => 10)) +
90 text_field_tag(field_name, '', tag_options.merge(:size => 10)) +
100 calendar_for(field_id)
91 calendar_for(field_id)
101 when "text"
92 when "text"
102 text_area_tag(field_name, '', tag_options.merge(:rows => 3))
93 text_area_tag(field_name, '', tag_options.merge(:rows => 3))
103 when "bool"
94 when "bool"
104 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
95 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
105 [l(:general_text_yes), '1'],
96 [l(:general_text_yes), '1'],
106 [l(:general_text_no), '0']]), tag_options)
97 [l(:general_text_no), '0']]), tag_options)
107 when "list"
98 when "list"
108 options = []
99 options = []
109 options << [l(:label_no_change_option), ''] unless custom_field.multiple?
100 options << [l(:label_no_change_option), ''] unless custom_field.multiple?
110 options << [l(:label_none), '__none__'] unless custom_field.is_required?
101 options << [l(:label_none), '__none__'] unless custom_field.is_required?
111 options += custom_field.possible_values_options(projects)
102 options += custom_field.possible_values_options(projects)
112 select_tag(field_name, options_for_select(options), tag_options.merge(:multiple => custom_field.multiple?))
103 select_tag(field_name, options_for_select(options), tag_options.merge(:multiple => custom_field.multiple?))
113 else
104 else
114 text_field_tag(field_name, '', tag_options)
105 text_field_tag(field_name, '', tag_options)
115 end
106 end
116 end
107 end
117
108
118 # Return a string used to display a custom value
109 # Return a string used to display a custom value
119 def show_value(custom_value)
110 def show_value(custom_value)
120 return "" unless custom_value
111 return "" unless custom_value
121 format_value(custom_value.value, custom_value.custom_field.field_format)
112 format_value(custom_value.value, custom_value.custom_field.field_format)
122 end
113 end
123
114
124 # Return a string used to display a custom value
115 # Return a string used to display a custom value
125 def format_value(value, field_format)
116 def format_value(value, field_format)
126 if value.is_a?(Array)
117 if value.is_a?(Array)
127 value.collect {|v| format_value(v, field_format)}.compact.sort.join(', ')
118 value.collect {|v| format_value(v, field_format)}.compact.sort.join(', ')
128 else
119 else
129 Redmine::CustomFieldFormat.format_value(value, field_format)
120 Redmine::CustomFieldFormat.format_value(value, field_format)
130 end
121 end
131 end
122 end
132
123
133 # Return an array of custom field formats which can be used in select_tag
124 # Return an array of custom field formats which can be used in select_tag
134 def custom_field_formats_for_select(custom_field)
125 def custom_field_formats_for_select(custom_field)
135 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
126 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
136 end
127 end
137
128
138 # Renders the custom_values in api views
129 # Renders the custom_values in api views
139 def render_api_custom_values(custom_values, api)
130 def render_api_custom_values(custom_values, api)
140 api.array :custom_fields do
131 api.array :custom_fields do
141 custom_values.each do |custom_value|
132 custom_values.each do |custom_value|
142 attrs = {:id => custom_value.custom_field_id, :name => custom_value.custom_field.name}
133 attrs = {:id => custom_value.custom_field_id, :name => custom_value.custom_field.name}
143 attrs.merge!(:multiple => true) if custom_value.custom_field.multiple?
134 attrs.merge!(:multiple => true) if custom_value.custom_field.multiple?
144 api.custom_field attrs do
135 api.custom_field attrs do
145 if custom_value.value.is_a?(Array)
136 if custom_value.value.is_a?(Array)
146 api.array :value do
137 api.array :value do
147 custom_value.value.each do |value|
138 custom_value.value.each do |value|
148 api.value value unless value.blank?
139 api.value value unless value.blank?
149 end
140 end
150 end
141 end
151 else
142 else
152 api.value custom_value.value
143 api.value custom_value.value
153 end
144 end
154 end
145 end
155 end
146 end
156 end unless custom_values.empty?
147 end unless custom_values.empty?
157 end
148 end
158 end
149 end
@@ -1,292 +1,313
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 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 acts_as_list :scope => 'type = \'#{self.class}\''
22 acts_as_list :scope => 'type = \'#{self.class}\''
23 serialize :possible_values
23 serialize :possible_values
24
24
25 validates_presence_of :name, :field_format
25 validates_presence_of :name, :field_format
26 validates_uniqueness_of :name, :scope => :type
26 validates_uniqueness_of :name, :scope => :type
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
32
33 CUSTOM_FIELDS_TABS = [
34 {:name => 'IssueCustomField', :partial => 'custom_fields/index',
35 :label => :label_issue_plural},
36 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index',
37 :label => :label_spent_time},
38 {:name => 'ProjectCustomField', :partial => 'custom_fields/index',
39 :label => :label_project_plural},
40 {:name => 'VersionCustomField', :partial => 'custom_fields/index',
41 :label => :label_version_plural},
42 {:name => 'UserCustomField', :partial => 'custom_fields/index',
43 :label => :label_user_plural},
44 {:name => 'GroupCustomField', :partial => 'custom_fields/index',
45 :label => :label_group_plural},
46 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index',
47 :label => TimeEntryActivity::OptionName},
48 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index',
49 :label => IssuePriority::OptionName},
50 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index',
51 :label => DocumentCategory::OptionName}
52 ]
53
33 def set_searchable
54 def set_searchable
34 # make sure these fields are not searchable
55 # make sure these fields are not searchable
35 self.searchable = false if %w(int float date bool).include?(field_format)
56 self.searchable = false if %w(int float date bool).include?(field_format)
36 # make sure only these fields can have multiple values
57 # make sure only these fields can have multiple values
37 self.multiple = false unless %w(list user version).include?(field_format)
58 self.multiple = false unless %w(list user version).include?(field_format)
38 true
59 true
39 end
60 end
40
61
41 def validate_custom_field
62 def validate_custom_field
42 if self.field_format == "list"
63 if self.field_format == "list"
43 errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty?
64 errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty?
44 errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
65 errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
45 end
66 end
46
67
47 if regexp.present?
68 if regexp.present?
48 begin
69 begin
49 Regexp.new(regexp)
70 Regexp.new(regexp)
50 rescue
71 rescue
51 errors.add(:regexp, :invalid)
72 errors.add(:regexp, :invalid)
52 end
73 end
53 end
74 end
54
75
55 if default_value.present? && !valid_field_value?(default_value)
76 if default_value.present? && !valid_field_value?(default_value)
56 errors.add(:default_value, :invalid)
77 errors.add(:default_value, :invalid)
57 end
78 end
58 end
79 end
59
80
60 def possible_values_options(obj=nil)
81 def possible_values_options(obj=nil)
61 case field_format
82 case field_format
62 when 'user', 'version'
83 when 'user', 'version'
63 if obj.respond_to?(:project) && obj.project
84 if obj.respond_to?(:project) && obj.project
64 case field_format
85 case field_format
65 when 'user'
86 when 'user'
66 obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
87 obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
67 when 'version'
88 when 'version'
68 obj.project.shared_versions.sort.collect {|u| [u.to_s, u.id.to_s]}
89 obj.project.shared_versions.sort.collect {|u| [u.to_s, u.id.to_s]}
69 end
90 end
70 elsif obj.is_a?(Array)
91 elsif obj.is_a?(Array)
71 obj.collect {|o| possible_values_options(o)}.reduce(:&)
92 obj.collect {|o| possible_values_options(o)}.reduce(:&)
72 else
93 else
73 []
94 []
74 end
95 end
75 when 'bool'
96 when 'bool'
76 [[l(:general_text_Yes), '1'], [l(:general_text_No), '0']]
97 [[l(:general_text_Yes), '1'], [l(:general_text_No), '0']]
77 else
98 else
78 possible_values || []
99 possible_values || []
79 end
100 end
80 end
101 end
81
102
82 def possible_values(obj=nil)
103 def possible_values(obj=nil)
83 case field_format
104 case field_format
84 when 'user', 'version'
105 when 'user', 'version'
85 possible_values_options(obj).collect(&:last)
106 possible_values_options(obj).collect(&:last)
86 when 'bool'
107 when 'bool'
87 ['1', '0']
108 ['1', '0']
88 else
109 else
89 values = super()
110 values = super()
90 if values.is_a?(Array)
111 if values.is_a?(Array)
91 values.each do |value|
112 values.each do |value|
92 value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
113 value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
93 end
114 end
94 end
115 end
95 values || []
116 values || []
96 end
117 end
97 end
118 end
98
119
99 # Makes possible_values accept a multiline string
120 # Makes possible_values accept a multiline string
100 def possible_values=(arg)
121 def possible_values=(arg)
101 if arg.is_a?(Array)
122 if arg.is_a?(Array)
102 super(arg.compact.collect(&:strip).select {|v| !v.blank?})
123 super(arg.compact.collect(&:strip).select {|v| !v.blank?})
103 else
124 else
104 self.possible_values = arg.to_s.split(/[\n\r]+/)
125 self.possible_values = arg.to_s.split(/[\n\r]+/)
105 end
126 end
106 end
127 end
107
128
108 def cast_value(value)
129 def cast_value(value)
109 casted = nil
130 casted = nil
110 unless value.blank?
131 unless value.blank?
111 case field_format
132 case field_format
112 when 'string', 'text', 'list'
133 when 'string', 'text', 'list'
113 casted = value
134 casted = value
114 when 'date'
135 when 'date'
115 casted = begin; value.to_date; rescue; nil end
136 casted = begin; value.to_date; rescue; nil end
116 when 'bool'
137 when 'bool'
117 casted = (value == '1' ? true : false)
138 casted = (value == '1' ? true : false)
118 when 'int'
139 when 'int'
119 casted = value.to_i
140 casted = value.to_i
120 when 'float'
141 when 'float'
121 casted = value.to_f
142 casted = value.to_f
122 when 'user', 'version'
143 when 'user', 'version'
123 casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
144 casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
124 end
145 end
125 end
146 end
126 casted
147 casted
127 end
148 end
128
149
129 def value_from_keyword(keyword, customized)
150 def value_from_keyword(keyword, customized)
130 possible_values_options = possible_values_options(customized)
151 possible_values_options = possible_values_options(customized)
131 if possible_values_options.present?
152 if possible_values_options.present?
132 keyword = keyword.to_s.downcase
153 keyword = keyword.to_s.downcase
133 possible_values_options.detect {|text, id| text.downcase == keyword}.try(:last)
154 possible_values_options.detect {|text, id| text.downcase == keyword}.try(:last)
134 else
155 else
135 keyword
156 keyword
136 end
157 end
137 end
158 end
138
159
139 # Returns a ORDER BY clause that can used to sort customized
160 # Returns a ORDER BY clause that can used to sort customized
140 # objects by their value of the custom field.
161 # objects by their value of the custom field.
141 # Returns nil if the custom field can not be used for sorting.
162 # Returns nil if the custom field can not be used for sorting.
142 def order_statement
163 def order_statement
143 return nil if multiple?
164 return nil if multiple?
144 case field_format
165 case field_format
145 when 'string', 'text', 'list', 'date', 'bool'
166 when 'string', 'text', 'list', 'date', 'bool'
146 # COALESCE is here to make sure that blank and NULL values are sorted equally
167 # COALESCE is here to make sure that blank and NULL values are sorted equally
147 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
168 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
148 " WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
169 " WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
149 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
170 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
150 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
171 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
151 when 'int', 'float'
172 when 'int', 'float'
152 # Make the database cast values into numeric
173 # Make the database cast values into numeric
153 # Postgresql will raise an error if a value can not be casted!
174 # Postgresql will raise an error if a value can not be casted!
154 # CustomValue validations should ensure that it doesn't occur
175 # CustomValue validations should ensure that it doesn't occur
155 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
176 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
156 " WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
177 " WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
157 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
178 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
158 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
179 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
159 when 'user', 'version'
180 when 'user', 'version'
160 value_class.fields_for_order_statement(value_join_alias)
181 value_class.fields_for_order_statement(value_join_alias)
161 else
182 else
162 nil
183 nil
163 end
184 end
164 end
185 end
165
186
166 # Returns a GROUP BY clause that can used to group by custom value
187 # Returns a GROUP BY clause that can used to group by custom value
167 # Returns nil if the custom field can not be used for grouping.
188 # Returns nil if the custom field can not be used for grouping.
168 def group_statement
189 def group_statement
169 return nil if multiple?
190 return nil if multiple?
170 case field_format
191 case field_format
171 when 'list', 'date', 'bool', 'int'
192 when 'list', 'date', 'bool', 'int'
172 order_statement
193 order_statement
173 when 'user', 'version'
194 when 'user', 'version'
174 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
195 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
175 " WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
196 " WHERE cv_sort.customized_type='#{self.class.customized_class.base_class.name}'" +
176 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
197 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
177 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
198 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
178 else
199 else
179 nil
200 nil
180 end
201 end
181 end
202 end
182
203
183 def join_for_order_statement
204 def join_for_order_statement
184 case field_format
205 case field_format
185 when 'user', 'version'
206 when 'user', 'version'
186 "LEFT OUTER JOIN #{CustomValue.table_name} #{join_alias}" +
207 "LEFT OUTER JOIN #{CustomValue.table_name} #{join_alias}" +
187 " ON #{join_alias}.customized_type = '#{self.class.customized_class.base_class.name}'" +
208 " ON #{join_alias}.customized_type = '#{self.class.customized_class.base_class.name}'" +
188 " AND #{join_alias}.customized_id = #{self.class.customized_class.table_name}.id" +
209 " AND #{join_alias}.customized_id = #{self.class.customized_class.table_name}.id" +
189 " AND #{join_alias}.custom_field_id = #{id}" +
210 " AND #{join_alias}.custom_field_id = #{id}" +
190 " AND #{join_alias}.value <> ''" +
211 " AND #{join_alias}.value <> ''" +
191 " AND #{join_alias}.id = (SELECT max(#{join_alias}_2.id) FROM #{CustomValue.table_name} #{join_alias}_2" +
212 " AND #{join_alias}.id = (SELECT max(#{join_alias}_2.id) FROM #{CustomValue.table_name} #{join_alias}_2" +
192 " WHERE #{join_alias}_2.customized_type = #{join_alias}.customized_type" +
213 " WHERE #{join_alias}_2.customized_type = #{join_alias}.customized_type" +
193 " AND #{join_alias}_2.customized_id = #{join_alias}.customized_id" +
214 " AND #{join_alias}_2.customized_id = #{join_alias}.customized_id" +
194 " AND #{join_alias}_2.custom_field_id = #{join_alias}.custom_field_id)" +
215 " AND #{join_alias}_2.custom_field_id = #{join_alias}.custom_field_id)" +
195 " LEFT OUTER JOIN #{value_class.table_name} #{value_join_alias}" +
216 " LEFT OUTER JOIN #{value_class.table_name} #{value_join_alias}" +
196 " ON CAST(#{join_alias}.value as decimal(60,0)) = #{value_join_alias}.id"
217 " ON CAST(#{join_alias}.value as decimal(60,0)) = #{value_join_alias}.id"
197 else
218 else
198 nil
219 nil
199 end
220 end
200 end
221 end
201
222
202 def join_alias
223 def join_alias
203 "cf_#{id}"
224 "cf_#{id}"
204 end
225 end
205
226
206 def value_join_alias
227 def value_join_alias
207 join_alias + "_" + field_format
228 join_alias + "_" + field_format
208 end
229 end
209
230
210 def <=>(field)
231 def <=>(field)
211 position <=> field.position
232 position <=> field.position
212 end
233 end
213
234
214 # Returns the class that values represent
235 # Returns the class that values represent
215 def value_class
236 def value_class
216 case field_format
237 case field_format
217 when 'user', 'version'
238 when 'user', 'version'
218 field_format.classify.constantize
239 field_format.classify.constantize
219 else
240 else
220 nil
241 nil
221 end
242 end
222 end
243 end
223
244
224 def self.customized_class
245 def self.customized_class
225 self.name =~ /^(.+)CustomField$/
246 self.name =~ /^(.+)CustomField$/
226 begin; $1.constantize; rescue nil; end
247 begin; $1.constantize; rescue nil; end
227 end
248 end
228
249
229 # to move in project_custom_field
250 # to move in project_custom_field
230 def self.for_all
251 def self.for_all
231 find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
252 find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
232 end
253 end
233
254
234 def type_name
255 def type_name
235 nil
256 nil
236 end
257 end
237
258
238 # Returns the error messages for the given value
259 # Returns the error messages for the given value
239 # or an empty array if value is a valid value for the custom field
260 # or an empty array if value is a valid value for the custom field
240 def validate_field_value(value)
261 def validate_field_value(value)
241 errs = []
262 errs = []
242 if value.is_a?(Array)
263 if value.is_a?(Array)
243 if !multiple?
264 if !multiple?
244 errs << ::I18n.t('activerecord.errors.messages.invalid')
265 errs << ::I18n.t('activerecord.errors.messages.invalid')
245 end
266 end
246 if is_required? && value.detect(&:present?).nil?
267 if is_required? && value.detect(&:present?).nil?
247 errs << ::I18n.t('activerecord.errors.messages.blank')
268 errs << ::I18n.t('activerecord.errors.messages.blank')
248 end
269 end
249 value.each {|v| errs += validate_field_value_format(v)}
270 value.each {|v| errs += validate_field_value_format(v)}
250 else
271 else
251 if is_required? && value.blank?
272 if is_required? && value.blank?
252 errs << ::I18n.t('activerecord.errors.messages.blank')
273 errs << ::I18n.t('activerecord.errors.messages.blank')
253 end
274 end
254 errs += validate_field_value_format(value)
275 errs += validate_field_value_format(value)
255 end
276 end
256 errs
277 errs
257 end
278 end
258
279
259 # Returns true if value is a valid value for the custom field
280 # Returns true if value is a valid value for the custom field
260 def valid_field_value?(value)
281 def valid_field_value?(value)
261 validate_field_value(value).empty?
282 validate_field_value(value).empty?
262 end
283 end
263
284
264 def format_in?(*args)
285 def format_in?(*args)
265 args.include?(field_format)
286 args.include?(field_format)
266 end
287 end
267
288
268 protected
289 protected
269
290
270 # Returns the error message for the given value regarding its format
291 # Returns the error message for the given value regarding its format
271 def validate_field_value_format(value)
292 def validate_field_value_format(value)
272 errs = []
293 errs = []
273 if value.present?
294 if value.present?
274 errs << ::I18n.t('activerecord.errors.messages.invalid') unless regexp.blank? or value =~ Regexp.new(regexp)
295 errs << ::I18n.t('activerecord.errors.messages.invalid') unless regexp.blank? or value =~ Regexp.new(regexp)
275 errs << ::I18n.t('activerecord.errors.messages.too_short', :count => min_length) if min_length > 0 and value.length < min_length
296 errs << ::I18n.t('activerecord.errors.messages.too_short', :count => min_length) if min_length > 0 and value.length < min_length
276 errs << ::I18n.t('activerecord.errors.messages.too_long', :count => max_length) if max_length > 0 and value.length > max_length
297 errs << ::I18n.t('activerecord.errors.messages.too_long', :count => max_length) if max_length > 0 and value.length > max_length
277
298
278 # Format specific validations
299 # Format specific validations
279 case field_format
300 case field_format
280 when 'int'
301 when 'int'
281 errs << ::I18n.t('activerecord.errors.messages.not_a_number') unless value =~ /^[+-]?\d+$/
302 errs << ::I18n.t('activerecord.errors.messages.not_a_number') unless value =~ /^[+-]?\d+$/
282 when 'float'
303 when 'float'
283 begin; Kernel.Float(value); rescue; errs << ::I18n.t('activerecord.errors.messages.invalid') end
304 begin; Kernel.Float(value); rescue; errs << ::I18n.t('activerecord.errors.messages.invalid') end
284 when 'date'
305 when 'date'
285 errs << ::I18n.t('activerecord.errors.messages.not_a_date') unless value =~ /^\d{4}-\d{2}-\d{2}$/ && begin; value.to_date; rescue; false end
306 errs << ::I18n.t('activerecord.errors.messages.not_a_date') unless value =~ /^\d{4}-\d{2}-\d{2}$/ && begin; value.to_date; rescue; false end
286 when 'list'
307 when 'list'
287 errs << ::I18n.t('activerecord.errors.messages.inclusion') unless possible_values.include?(value)
308 errs << ::I18n.t('activerecord.errors.messages.inclusion') unless possible_values.include?(value)
288 end
309 end
289 end
310 end
290 errs
311 errs
291 end
312 end
292 end
313 end
General Comments 0
You need to be logged in to leave comments. Login now