##// END OF EJS Templates
Fixes #possible_values_options when given an object with nil project....
Jean-Philippe Lang -
r5153:2be6f54f2345
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)
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'
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
@@ -1,65 +1,70
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 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class CustomFieldUserFormatTest < ActiveSupport::TestCase
20 class CustomFieldUserFormatTest < ActiveSupport::TestCase
21 fixtures :custom_fields, :projects, :members, :users, :member_roles, :trackers, :issues
21 fixtures :custom_fields, :projects, :members, :users, :member_roles, :trackers, :issues
22
22
23 def setup
23 def setup
24 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user')
24 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user')
25 end
25 end
26
26
27 def test_possible_values_with_no_arguments
27 def test_possible_values_with_no_arguments
28 assert_equal [], @field.possible_values
28 assert_equal [], @field.possible_values
29 assert_equal [], @field.possible_values(nil)
29 assert_equal [], @field.possible_values(nil)
30 end
30 end
31
31
32 def test_possible_values_with_project_resource
32 def test_possible_values_with_project_resource
33 project = Project.find(1)
33 project = Project.find(1)
34 possible_values = @field.possible_values(project.issues.first)
34 possible_values = @field.possible_values(project.issues.first)
35 assert possible_values.any?
35 assert possible_values.any?
36 assert_equal project.users.sort.collect(&:id).map(&:to_s), possible_values
36 assert_equal project.users.sort.collect(&:id).map(&:to_s), possible_values
37 end
37 end
38
38
39 def test_possible_values_with_nil_project_resource
40 project = Project.find(1)
41 assert_equal [], @field.possible_values(Issue.new)
42 end
43
39 def test_possible_values_options_with_no_arguments
44 def test_possible_values_options_with_no_arguments
40 assert_equal [], @field.possible_values_options
45 assert_equal [], @field.possible_values_options
41 assert_equal [], @field.possible_values_options(nil)
46 assert_equal [], @field.possible_values_options(nil)
42 end
47 end
43
48
44 def test_possible_values_options_with_project_resource
49 def test_possible_values_options_with_project_resource
45 project = Project.find(1)
50 project = Project.find(1)
46 possible_values_options = @field.possible_values_options(project.issues.first)
51 possible_values_options = @field.possible_values_options(project.issues.first)
47 assert possible_values_options.any?
52 assert possible_values_options.any?
48 assert_equal project.users.sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
53 assert_equal project.users.sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
49 end
54 end
50
55
51 def test_cast_blank_value
56 def test_cast_blank_value
52 assert_equal nil, @field.cast_value(nil)
57 assert_equal nil, @field.cast_value(nil)
53 assert_equal nil, @field.cast_value("")
58 assert_equal nil, @field.cast_value("")
54 end
59 end
55
60
56 def test_cast_valid_value
61 def test_cast_valid_value
57 user = @field.cast_value("2")
62 user = @field.cast_value("2")
58 assert_kind_of User, user
63 assert_kind_of User, user
59 assert_equal User.find(2), user
64 assert_equal User.find(2), user
60 end
65 end
61
66
62 def test_cast_invalid_value
67 def test_cast_invalid_value
63 assert_equal nil, @field.cast_value("187")
68 assert_equal nil, @field.cast_value("187")
64 end
69 end
65 end
70 end
General Comments 0
You need to be logged in to leave comments. Login now