@@ -0,0 +1,50 | |||
|
1 | # Redmine - project management software | |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
|
3 | # | |
|
4 | # This program is free software; you can redistribute it and/or | |
|
5 | # modify it under the terms of the GNU General Public License | |
|
6 | # as published by the Free Software Foundation; either version 2 | |
|
7 | # of the License, or (at your option) any later version. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
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 | |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
|
17 | ||
|
18 | class CustomFieldValue | |
|
19 | attr_accessor :custom_field, :customized, :value | |
|
20 | ||
|
21 | def custom_field_id | |
|
22 | custom_field.id | |
|
23 | end | |
|
24 | ||
|
25 | def true? | |
|
26 | self.value == '1' | |
|
27 | end | |
|
28 | ||
|
29 | def editable? | |
|
30 | custom_field.editable? | |
|
31 | end | |
|
32 | ||
|
33 | def visible? | |
|
34 | custom_field.visible? | |
|
35 | end | |
|
36 | ||
|
37 | def required? | |
|
38 | custom_field.is_required? | |
|
39 | end | |
|
40 | ||
|
41 | def to_s | |
|
42 | value.to_s | |
|
43 | end | |
|
44 | ||
|
45 | def validate_value | |
|
46 | custom_field.validate_field_value(value).each do |message| | |
|
47 | customized.errors.add(:base, custom_field.name + ' ' + message) | |
|
48 | end | |
|
49 | end | |
|
50 | end |
@@ -1,7 +1,7 | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | # |
|
3 | 3 | # Redmine - project management software |
|
4 |
# Copyright (C) 2006-201 |
|
|
4 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
|
5 | 5 | # |
|
6 | 6 | # This program is free software; you can redistribute it and/or |
|
7 | 7 | # modify it under the terms of the GNU General Public License |
@@ -61,8 +61,7 module CustomFieldsHelper | |||
|
61 | 61 | def custom_field_label_tag(name, custom_value) |
|
62 | 62 | content_tag "label", h(custom_value.custom_field.name) + |
|
63 | 63 | (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""), |
|
64 |
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}" |
|
|
65 | :class => (custom_value.errors.empty? ? nil : "error" ) | |
|
64 | :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}" | |
|
66 | 65 | end |
|
67 | 66 | |
|
68 | 67 | # Return custom field tag with its label tag |
@@ -1,5 +1,5 | |||
|
1 | 1 | # Redmine - project management software |
|
2 |
# Copyright (C) 2006-201 |
|
|
2 | # Copyright (C) 2006-2012 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 |
@@ -27,7 +27,7 class CustomField < ActiveRecord::Base | |||
|
27 | 27 | validates_length_of :name, :maximum => 30 |
|
28 | 28 | validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats |
|
29 | 29 | |
|
30 |
validate :validate_ |
|
|
30 | validate :validate_custom_field | |
|
31 | 31 | before_validation :set_searchable |
|
32 | 32 | |
|
33 | 33 | def initialize(attributes=nil, *args) |
@@ -41,7 +41,7 class CustomField < ActiveRecord::Base | |||
|
41 | 41 | true |
|
42 | 42 | end |
|
43 | 43 | |
|
44 |
def validate_ |
|
|
44 | def validate_custom_field | |
|
45 | 45 | if self.field_format == "list" |
|
46 | 46 | errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty? |
|
47 | 47 | errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array |
@@ -55,10 +55,9 class CustomField < ActiveRecord::Base | |||
|
55 | 55 | end |
|
56 | 56 | end |
|
57 | 57 | |
|
58 |
|
|
|
59 | v = CustomValue.new(:custom_field => self.clone, :value => default_value, :customized => nil) | |
|
60 | v.custom_field.is_required = false | |
|
61 | errors.add(:default_value, :invalid) unless v.valid? | |
|
58 | unless valid_field_value?(default_value) | |
|
59 | errors.add(:default_value, :invalid) | |
|
60 | end | |
|
62 | 61 | end |
|
63 | 62 | |
|
64 | 63 | def possible_values_options(obj=nil) |
@@ -161,4 +160,45 class CustomField < ActiveRecord::Base | |||
|
161 | 160 | def type_name |
|
162 | 161 | nil |
|
163 | 162 | end |
|
163 | ||
|
164 | # Returns the error message for the given value | |
|
165 | # or an empty array if value is a valid value for the custom field | |
|
166 | def validate_field_value(value) | |
|
167 | errs = [] | |
|
168 | if is_required? && value.blank? | |
|
169 | errs << ::I18n.t('activerecord.errors.messages.blank') | |
|
170 | end | |
|
171 | errs += validate_field_value_format(value) | |
|
172 | errs | |
|
173 | end | |
|
174 | ||
|
175 | # Returns true if value is a valid value for the custom field | |
|
176 | def valid_field_value?(value) | |
|
177 | validate_field_value(value).empty? | |
|
178 | end | |
|
179 | ||
|
180 | protected | |
|
181 | ||
|
182 | # Returns the error message for the given value regarding its format | |
|
183 | def validate_field_value_format(value) | |
|
184 | errs = [] | |
|
185 | if value.present? | |
|
186 | errs << ::I18n.t('activerecord.errors.messages.invalid') unless regexp.blank? or value =~ Regexp.new(regexp) | |
|
187 | errs << ::I18n.t('activerecord.errors.messages.too_short', :count => min_length) if min_length > 0 and value.length < min_length | |
|
188 | errs << ::I18n.t('activerecord.errors.messages.too_long', :count => max_length) if max_length > 0 and value.length > max_length | |
|
189 | ||
|
190 | # Format specific validations | |
|
191 | case field_format | |
|
192 | when 'int' | |
|
193 | errs << ::I18n.t('activerecord.errors.messages.not_a_number') unless value =~ /^[+-]?\d+$/ | |
|
194 | when 'float' | |
|
195 | begin; Kernel.Float(value); rescue; errs << ::I18n.t('activerecord.errors.messages.invalid') end | |
|
196 | when 'date' | |
|
197 | errs << ::I18n.t('activerecord.errors.messages.not_a_date') unless value =~ /^\d{4}-\d{2}-\d{2}$/ && begin; value.to_date; rescue; false end | |
|
198 | when 'list' | |
|
199 | errs << ::I18n.t('activerecord.errors.messages.inclusion') unless possible_values.include?(value) | |
|
200 | end | |
|
201 | end | |
|
202 | errs | |
|
203 | end | |
|
164 | 204 | end |
@@ -1,5 +1,5 | |||
|
1 | 1 | # Redmine - project management software |
|
2 |
# Copyright (C) 2006-201 |
|
|
2 | # Copyright (C) 2006-2012 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 |
@@ -19,8 +19,6 class CustomValue < ActiveRecord::Base | |||
|
19 | 19 | belongs_to :custom_field |
|
20 | 20 | belongs_to :customized, :polymorphic => true |
|
21 | 21 | |
|
22 | validate :validate_custom_value | |
|
23 | ||
|
24 | 22 | def initialize(attributes=nil, *args) |
|
25 | 23 | super |
|
26 | 24 | if new_record? && custom_field && (customized_type.blank? || (customized && customized.new_record?)) |
@@ -48,27 +46,4 class CustomValue < ActiveRecord::Base | |||
|
48 | 46 | def to_s |
|
49 | 47 | value.to_s |
|
50 | 48 | end |
|
51 | ||
|
52 | protected | |
|
53 | def validate_custom_value | |
|
54 | if value.blank? | |
|
55 | errors.add(:value, :blank) if custom_field.is_required? and value.blank? | |
|
56 | else | |
|
57 | errors.add(:value, :invalid) unless custom_field.regexp.blank? or value =~ Regexp.new(custom_field.regexp) | |
|
58 | errors.add(:value, :too_short, :count => custom_field.min_length) if custom_field.min_length > 0 and value.length < custom_field.min_length | |
|
59 | errors.add(:value, :too_long, :count => custom_field.max_length) if custom_field.max_length > 0 and value.length > custom_field.max_length | |
|
60 | ||
|
61 | # Format specific validations | |
|
62 | case custom_field.field_format | |
|
63 | when 'int' | |
|
64 | errors.add(:value, :not_a_number) unless value =~ /^[+-]?\d+$/ | |
|
65 | when 'float' | |
|
66 | begin; Kernel.Float(value); rescue; errors.add(:value, :invalid) end | |
|
67 | when 'date' | |
|
68 | errors.add(:value, :not_a_date) unless value =~ /^\d{4}-\d{2}-\d{2}$/ && begin; value.to_date; rescue; false end | |
|
69 | when 'list' | |
|
70 | errors.add(:value, :inclusion) unless custom_field.possible_values.include?(value) | |
|
71 | end | |
|
72 | end | |
|
73 | end | |
|
74 | 49 | end |
@@ -16,36 +16,6 module ActiveRecord | |||
|
16 | 16 | end |
|
17 | 17 | end |
|
18 | 18 | |
|
19 | module ActiveRecord | |
|
20 | class Errors | |
|
21 | def full_messages(options = {}) | |
|
22 | full_messages = [] | |
|
23 | ||
|
24 | @errors.each_key do |attr| | |
|
25 | @errors[attr].each do |message| | |
|
26 | next unless message | |
|
27 | ||
|
28 | if attr == "base" | |
|
29 | full_messages << message | |
|
30 | elsif attr == "custom_values" | |
|
31 | # Replace the generic "custom values is invalid" | |
|
32 | # with the errors on custom values | |
|
33 | @base.custom_values.each do |value| | |
|
34 | value.errors.each do |attr, msg| | |
|
35 | full_messages << value.custom_field.name + ' ' + msg | |
|
36 | end | |
|
37 | end | |
|
38 | else | |
|
39 | attr_name = @base.class.human_attribute_name(attr) | |
|
40 | full_messages << attr_name + ' ' + message.to_s | |
|
41 | end | |
|
42 | end | |
|
43 | end | |
|
44 | full_messages | |
|
45 | end | |
|
46 | end | |
|
47 | end | |
|
48 | ||
|
49 | 19 | module ActionView |
|
50 | 20 | module Helpers |
|
51 | 21 | module DateHelper |
@@ -1,5 +1,5 | |||
|
1 | 1 | # Redmine - project management software |
|
2 |
# Copyright (C) 2006-201 |
|
|
2 | # Copyright (C) 2006-2012 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 |
@@ -1308,17 +1308,18 class IssuesControllerTest < ActionController::TestCase | |||
|
1308 | 1308 | field.update_attribute(:is_required, true) |
|
1309 | 1309 | |
|
1310 | 1310 | @request.session[:user_id] = 2 |
|
1311 | post :create, :project_id => 1, | |
|
1312 | :issue => {:tracker_id => 1, | |
|
1313 | :subject => 'This is the test_new issue', | |
|
1314 |
: |
|
|
1315 | :priority_id => 5} | |
|
1311 | assert_no_difference 'Issue.count' do | |
|
1312 | post :create, :project_id => 1, | |
|
1313 | :issue => {:tracker_id => 1, | |
|
1314 | :subject => 'This is the test_new issue', | |
|
1315 | :description => 'This is the description', | |
|
1316 | :priority_id => 5} | |
|
1317 | end | |
|
1316 | 1318 | assert_response :success |
|
1317 | 1319 | assert_template 'new' |
|
1318 | 1320 | issue = assigns(:issue) |
|
1319 | 1321 | assert_not_nil issue |
|
1320 | assert_equal I18n.translate('activerecord.errors.messages.invalid'), | |
|
1321 | issue.errors[:custom_values].to_s | |
|
1322 | assert_error_tag :content => /Database can't be blank/ | |
|
1322 | 1323 | end |
|
1323 | 1324 | |
|
1324 | 1325 | def test_post_create_with_watchers |
@@ -1,5 +1,5 | |||
|
1 | 1 | # Redmine - project management software |
|
2 |
# Copyright (C) 2006-201 |
|
|
2 | # Copyright (C) 2006-2012 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 |
@@ -44,6 +44,14 class CustomFieldTest < ActiveSupport::TestCase | |||
|
44 | 44 | assert field.save |
|
45 | 45 | end |
|
46 | 46 | |
|
47 | def test_default_value_should_be_validated | |
|
48 | field = CustomField.new(:name => 'Test', :field_format => 'int') | |
|
49 | field.default_value = 'abc' | |
|
50 | assert !field.valid? | |
|
51 | field.default_value = '6' | |
|
52 | assert field.valid? | |
|
53 | end | |
|
54 | ||
|
47 | 55 | def test_possible_values_should_accept_an_array |
|
48 | 56 | field = CustomField.new |
|
49 | 57 | field.possible_values = ["One value", ""] |
@@ -85,4 +93,75 class CustomFieldTest < ActiveSupport::TestCase | |||
|
85 | 93 | def test_new_subclass_instance_with_non_subclass_name_should_return_nil |
|
86 | 94 | assert_nil CustomField.new_subclass_instance('Project') |
|
87 | 95 | end |
|
96 | ||
|
97 | def test_string_field_validation_with_blank_value | |
|
98 | f = CustomField.new(:field_format => 'string') | |
|
99 | ||
|
100 | assert f.valid_field_value?(nil) | |
|
101 | assert f.valid_field_value?('') | |
|
102 | ||
|
103 | f.is_required = true | |
|
104 | assert !f.valid_field_value?(nil) | |
|
105 | assert !f.valid_field_value?('') | |
|
106 | end | |
|
107 | ||
|
108 | def test_string_field_validation_with_min_and_max_lengths | |
|
109 | f = CustomField.new(:field_format => 'string', :min_length => 2, :max_length => 5) | |
|
110 | ||
|
111 | assert f.valid_field_value?(nil) | |
|
112 | assert f.valid_field_value?('') | |
|
113 | assert f.valid_field_value?('a' * 2) | |
|
114 | assert !f.valid_field_value?('a') | |
|
115 | assert !f.valid_field_value?('a' * 6) | |
|
116 | end | |
|
117 | ||
|
118 | def test_string_field_validation_with_regexp | |
|
119 | f = CustomField.new(:field_format => 'string', :regexp => '^[A-Z0-9]*$') | |
|
120 | ||
|
121 | assert f.valid_field_value?(nil) | |
|
122 | assert f.valid_field_value?('') | |
|
123 | assert f.valid_field_value?('ABC') | |
|
124 | assert !f.valid_field_value?('abc') | |
|
125 | end | |
|
126 | ||
|
127 | def test_date_field_validation | |
|
128 | f = CustomField.new(:field_format => 'date') | |
|
129 | ||
|
130 | assert f.valid_field_value?(nil) | |
|
131 | assert f.valid_field_value?('') | |
|
132 | assert f.valid_field_value?('1975-07-14') | |
|
133 | assert !f.valid_field_value?('1975-07-33') | |
|
134 | assert !f.valid_field_value?('abc') | |
|
135 | end | |
|
136 | ||
|
137 | def test_list_field_validation | |
|
138 | f = CustomField.new(:field_format => 'list', :possible_values => ['value1', 'value2']) | |
|
139 | ||
|
140 | assert f.valid_field_value?(nil) | |
|
141 | assert f.valid_field_value?('') | |
|
142 | assert f.valid_field_value?('value2') | |
|
143 | assert !f.valid_field_value?('abc') | |
|
144 | end | |
|
145 | ||
|
146 | def test_int_field_validation | |
|
147 | f = CustomField.new(:field_format => 'int') | |
|
148 | ||
|
149 | assert f.valid_field_value?(nil) | |
|
150 | assert f.valid_field_value?('') | |
|
151 | assert f.valid_field_value?('123') | |
|
152 | assert f.valid_field_value?('+123') | |
|
153 | assert f.valid_field_value?('-123') | |
|
154 | assert !f.valid_field_value?('6abc') | |
|
155 | end | |
|
156 | ||
|
157 | def test_float_field_validation | |
|
158 | f = CustomField.new(:field_format => 'float') | |
|
159 | ||
|
160 | assert f.valid_field_value?(nil) | |
|
161 | assert f.valid_field_value?('') | |
|
162 | assert f.valid_field_value?('11.2') | |
|
163 | assert f.valid_field_value?('-6.250') | |
|
164 | assert f.valid_field_value?('5') | |
|
165 | assert !f.valid_field_value?('6abc') | |
|
166 | end | |
|
88 | 167 | end |
@@ -1,5 +1,5 | |||
|
1 | 1 | # Redmine - project management software |
|
2 |
# Copyright (C) 2006-201 |
|
|
2 | # Copyright (C) 2006-2012 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 |
@@ -20,92 +20,6 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class CustomValueTest < ActiveSupport::TestCase |
|
21 | 21 | fixtures :custom_fields, :custom_values, :users |
|
22 | 22 | |
|
23 | def test_string_field_validation_with_blank_value | |
|
24 | f = CustomField.new(:field_format => 'string') | |
|
25 | v = CustomValue.new(:custom_field => f) | |
|
26 | ||
|
27 | v.value = nil | |
|
28 | assert v.valid? | |
|
29 | v.value = '' | |
|
30 | assert v.valid? | |
|
31 | ||
|
32 | f.is_required = true | |
|
33 | v.value = nil | |
|
34 | assert !v.valid? | |
|
35 | v.value = '' | |
|
36 | assert !v.valid? | |
|
37 | end | |
|
38 | ||
|
39 | def test_string_field_validation_with_min_and_max_lengths | |
|
40 | f = CustomField.new(:field_format => 'string', :min_length => 2, :max_length => 5) | |
|
41 | v = CustomValue.new(:custom_field => f, :value => '') | |
|
42 | assert v.valid? | |
|
43 | v.value = 'a' | |
|
44 | assert !v.valid? | |
|
45 | v.value = 'a' * 2 | |
|
46 | assert v.valid? | |
|
47 | v.value = 'a' * 6 | |
|
48 | assert !v.valid? | |
|
49 | end | |
|
50 | ||
|
51 | def test_string_field_validation_with_regexp | |
|
52 | f = CustomField.new(:field_format => 'string', :regexp => '^[A-Z0-9]*$') | |
|
53 | v = CustomValue.new(:custom_field => f, :value => '') | |
|
54 | assert v.valid? | |
|
55 | v.value = 'abc' | |
|
56 | assert !v.valid? | |
|
57 | v.value = 'ABC' | |
|
58 | assert v.valid? | |
|
59 | end | |
|
60 | ||
|
61 | def test_date_field_validation | |
|
62 | f = CustomField.new(:field_format => 'date') | |
|
63 | v = CustomValue.new(:custom_field => f, :value => '') | |
|
64 | assert v.valid? | |
|
65 | v.value = 'abc' | |
|
66 | assert !v.valid? | |
|
67 | v.value = '1975-07-33' | |
|
68 | assert !v.valid? | |
|
69 | v.value = '1975-07-14' | |
|
70 | assert v.valid? | |
|
71 | end | |
|
72 | ||
|
73 | def test_list_field_validation | |
|
74 | f = CustomField.new(:field_format => 'list', :possible_values => ['value1', 'value2']) | |
|
75 | v = CustomValue.new(:custom_field => f, :value => '') | |
|
76 | assert v.valid? | |
|
77 | v.value = 'abc' | |
|
78 | assert !v.valid? | |
|
79 | v.value = 'value2' | |
|
80 | assert v.valid? | |
|
81 | end | |
|
82 | ||
|
83 | def test_int_field_validation | |
|
84 | f = CustomField.new(:field_format => 'int') | |
|
85 | v = CustomValue.new(:custom_field => f, :value => '') | |
|
86 | assert v.valid? | |
|
87 | v.value = 'abc' | |
|
88 | assert !v.valid? | |
|
89 | v.value = '123' | |
|
90 | assert v.valid? | |
|
91 | v.value = '+123' | |
|
92 | assert v.valid? | |
|
93 | v.value = '-123' | |
|
94 | assert v.valid? | |
|
95 | end | |
|
96 | ||
|
97 | def test_float_field_validation | |
|
98 | v = CustomValue.new(:customized => User.find(:first), :custom_field => UserCustomField.find_by_name('Money')) | |
|
99 | v.value = '11.2' | |
|
100 | assert v.save | |
|
101 | v.value = '' | |
|
102 | assert v.save | |
|
103 | v.value = '-6.250' | |
|
104 | assert v.save | |
|
105 | v.value = '6a' | |
|
106 | assert !v.save | |
|
107 | end | |
|
108 | ||
|
109 | 23 | def test_default_value |
|
110 | 24 | field = CustomField.find_by_default_value('Default string') |
|
111 | 25 | assert_not_nil field |
@@ -1,5 +1,5 | |||
|
1 | 1 | # Redmine - project management software |
|
2 |
# Copyright (C) 2006-201 |
|
|
2 | # Copyright (C) 2006-2012 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 |
@@ -29,6 +29,8 class IssueTest < ActiveSupport::TestCase | |||
|
29 | 29 | :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values, |
|
30 | 30 | :time_entries |
|
31 | 31 | |
|
32 | include Redmine::I18n | |
|
33 | ||
|
32 | 34 | def test_create |
|
33 | 35 | issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, |
|
34 | 36 | :status_id => 1, :priority => IssuePriority.all.first, |
@@ -48,6 +50,7 class IssueTest < ActiveSupport::TestCase | |||
|
48 | 50 | end |
|
49 | 51 | |
|
50 | 52 | def test_create_with_required_custom_field |
|
53 | set_language_if_valid 'en' | |
|
51 | 54 | field = IssueCustomField.find_by_name('Database') |
|
52 | 55 | field.update_attribute(:is_required, true) |
|
53 | 56 | |
@@ -57,18 +60,15 class IssueTest < ActiveSupport::TestCase | |||
|
57 | 60 | assert issue.available_custom_fields.include?(field) |
|
58 | 61 | # No value for the custom field |
|
59 | 62 | assert !issue.save |
|
60 | assert_equal I18n.translate('activerecord.errors.messages.invalid'), | |
|
61 | issue.errors[:custom_values].to_s | |
|
63 | assert_equal "Database can't be blank", issue.errors[:base].to_s | |
|
62 | 64 | # Blank value |
|
63 | 65 | issue.custom_field_values = { field.id => '' } |
|
64 | 66 | assert !issue.save |
|
65 | assert_equal I18n.translate('activerecord.errors.messages.invalid'), | |
|
66 | issue.errors[:custom_values].to_s | |
|
67 | assert_equal "Database can't be blank", issue.errors[:base].to_s | |
|
67 | 68 | # Invalid value |
|
68 | 69 | issue.custom_field_values = { field.id => 'SQLServer' } |
|
69 | 70 | assert !issue.save |
|
70 | assert_equal I18n.translate('activerecord.errors.messages.invalid'), | |
|
71 | issue.errors[:custom_values].to_s | |
|
71 | assert_equal "Database is not included in the list", issue.errors[:base].to_s | |
|
72 | 72 | # Valid value |
|
73 | 73 | issue.custom_field_values = { field.id => 'PostgreSQL' } |
|
74 | 74 | assert issue.save |
@@ -327,8 +327,7 class IssueTest < ActiveSupport::TestCase | |||
|
327 | 327 | attributes['tracker_id'] = '1' |
|
328 | 328 | issue = Issue.new(:project => Project.find(1)) |
|
329 | 329 | issue.attributes = attributes |
|
330 |
assert_ |
|
|
331 | assert_equal 'MySQL', issue.custom_value_for(1).value | |
|
330 | assert_equal 'MySQL', issue.custom_field_value(1) | |
|
332 | 331 | end |
|
333 | 332 | |
|
334 | 333 | def test_should_update_issue_with_disabled_tracker |
@@ -1,5 +1,5 | |||
|
1 | 1 | # Redmine - project management software |
|
2 |
# Copyright (C) 2006-201 |
|
|
2 | # Copyright (C) 2006-2012 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 |
@@ -20,6 +20,8 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class TimeEntryActivityTest < ActiveSupport::TestCase |
|
21 | 21 | fixtures :enumerations, :time_entries |
|
22 | 22 | |
|
23 | include Redmine::I18n | |
|
24 | ||
|
23 | 25 | def test_should_be_an_enumeration |
|
24 | 26 | assert TimeEntryActivity.ancestors.include?(Enumeration) |
|
25 | 27 | end |
@@ -44,13 +46,13 class TimeEntryActivityTest < ActiveSupport::TestCase | |||
|
44 | 46 | end |
|
45 | 47 | |
|
46 | 48 | def test_create_without_required_custom_field_should_fail |
|
49 | set_language_if_valid 'en' | |
|
47 | 50 | field = TimeEntryActivityCustomField.find_by_name('Billable') |
|
48 | 51 | field.update_attribute(:is_required, true) |
|
49 | 52 | |
|
50 | 53 | e = TimeEntryActivity.new(:name => 'Custom Data') |
|
51 | 54 | assert !e.save |
|
52 | assert_equal I18n.translate('activerecord.errors.messages.invalid'), | |
|
53 | e.errors[:custom_values].to_s | |
|
55 | assert_equal "Billable can't be blank", e.errors[:base].to_s | |
|
54 | 56 | end |
|
55 | 57 | |
|
56 | 58 | def test_create_with_required_custom_field_should_succeed |
@@ -62,7 +64,8 class TimeEntryActivityTest < ActiveSupport::TestCase | |||
|
62 | 64 | assert e.save |
|
63 | 65 | end |
|
64 | 66 | |
|
65 |
def test_update_ |
|
|
67 | def test_update_with_required_custom_field_change | |
|
68 | set_language_if_valid 'en' | |
|
66 | 69 | field = TimeEntryActivityCustomField.find_by_name('Billable') |
|
67 | 70 | field.update_attribute(:is_required, true) |
|
68 | 71 | |
@@ -73,7 +76,7 class TimeEntryActivityTest < ActiveSupport::TestCase | |||
|
73 | 76 | # Blanking custom field, save should fail |
|
74 | 77 | e.custom_field_values = {field.id => ""} |
|
75 | 78 | assert !e.save |
|
76 | assert e.errors[:custom_values] | |
|
79 | assert_equal "Billable can't be blank", e.errors[:base].to_s | |
|
77 | 80 | |
|
78 | 81 | # Update custom field to valid value, save should succeed |
|
79 | 82 | e.custom_field_values = {field.id => "0"} |
@@ -81,6 +84,5 class TimeEntryActivityTest < ActiveSupport::TestCase | |||
|
81 | 84 | e.reload |
|
82 | 85 | assert_equal "0", e.custom_value_for(field).value |
|
83 | 86 | end |
|
84 | ||
|
85 | 87 | end |
|
86 | 88 |
@@ -1,5 +1,5 | |||
|
1 | 1 | # Redmine - project management software |
|
2 |
# Copyright (C) 2006-201 |
|
|
2 | # Copyright (C) 2006-2012 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 |
@@ -30,12 +30,10 module Redmine | |||
|
30 | 30 | has_many :custom_values, :as => :customized, |
|
31 | 31 | :include => :custom_field, |
|
32 | 32 | :order => "#{CustomField.table_name}.position", |
|
33 | :dependent => :delete_all | |
|
34 | before_validation { |customized| customized.custom_field_values if customized.new_record? } | |
|
35 | # Trigger validation only if custom values were changed | |
|
36 | validates_associated :custom_values, :on => :update, :if => Proc.new { |customized| customized.custom_field_values_changed? } | |
|
33 | :dependent => :delete_all, | |
|
34 | :validate => false | |
|
37 | 35 | send :include, Redmine::Acts::Customizable::InstanceMethods |
|
38 | # Save custom values when saving the customized object | |
|
36 | validate :validate_custom_field_values | |
|
39 | 37 | after_save :save_custom_field_values |
|
40 | 38 | end |
|
41 | 39 | end |
@@ -66,15 +64,28 module Redmine | |||
|
66 | 64 | # Sets the values of the object's custom fields |
|
67 | 65 | # values is a hash like {'1' => 'foo', 2 => 'bar'} |
|
68 | 66 | def custom_field_values=(values) |
|
69 | @custom_field_values_changed = true | |
|
70 | 67 | values = values.stringify_keys |
|
71 | custom_field_values.each do |custom_value| | |
|
72 | custom_value.value = values[custom_value.custom_field_id.to_s] if values.has_key?(custom_value.custom_field_id.to_s) | |
|
73 | end if values.is_a?(Hash) | |
|
68 | ||
|
69 | custom_field_values.each do |custom_field_value| | |
|
70 | key = custom_field_value.custom_field_id.to_s | |
|
71 | if values.has_key?(key) | |
|
72 | value = values[key] | |
|
73 | custom_field_value.value = value | |
|
74 | end | |
|
75 | end | |
|
76 | @custom_field_values_changed = true | |
|
74 | 77 | end |
|
75 | 78 | |
|
76 | 79 | def custom_field_values |
|
77 | @custom_field_values ||= available_custom_fields.collect { |x| custom_values.detect { |v| v.custom_field == x } || custom_values.build(:customized => self, :custom_field => x, :value => nil) } | |
|
80 | @custom_field_values ||= available_custom_fields.collect do |field| | |
|
81 | x = CustomFieldValue.new | |
|
82 | x.custom_field = field | |
|
83 | x.customized = self | |
|
84 | cv = custom_values.detect { |v| v.custom_field == field } | |
|
85 | cv ||= custom_values.build(:customized => self, :custom_field => field, :value => nil) | |
|
86 | x.value = cv.value | |
|
87 | x | |
|
88 | end | |
|
78 | 89 | end |
|
79 | 90 | |
|
80 | 91 | def visible_custom_field_values |
@@ -90,18 +101,34 module Redmine | |||
|
90 | 101 | custom_values.detect {|v| v.custom_field_id == field_id } |
|
91 | 102 | end |
|
92 | 103 | |
|
104 | def custom_field_value(c) | |
|
105 | field_id = (c.is_a?(CustomField) ? c.id : c.to_i) | |
|
106 | custom_field_values.detect {|v| v.custom_field_id == field_id }.try(:value) | |
|
107 | end | |
|
108 | ||
|
109 | def validate_custom_field_values | |
|
110 | if new_record? || custom_field_values_changed? | |
|
111 | custom_field_values.each(&:validate_value) | |
|
112 | end | |
|
113 | end | |
|
114 | ||
|
93 | 115 | def save_custom_field_values |
|
94 |
|
|
|
95 |
custom_field_values.each |
|
|
116 | target_custom_values = [] | |
|
117 | custom_field_values.each do |custom_field_value| | |
|
118 | target = custom_values.detect {|cv| cv.custom_field == custom_field_value.custom_field} | |
|
119 | target ||= custom_values.build(:customized => self, :custom_field => custom_field_value.custom_field) | |
|
120 | target.value = custom_field_value.value | |
|
121 | target_custom_values << target | |
|
122 | end | |
|
123 | self.custom_values = target_custom_values | |
|
124 | custom_values.each(&:save) | |
|
96 | 125 | @custom_field_values_changed = false |
|
97 | @custom_field_values = nil | |
|
126 | true | |
|
98 | 127 | end |
|
99 | 128 | |
|
100 | 129 | def reset_custom_values! |
|
101 | 130 | @custom_field_values = nil |
|
102 | 131 | @custom_field_values_changed = true |
|
103 | values = custom_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h} | |
|
104 | custom_values.each {|cv| cv.destroy unless custom_field_values.include?(cv)} | |
|
105 | 132 | end |
|
106 | 133 | |
|
107 | 134 | module ClassMethods |
General Comments 0
You need to be logged in to leave comments.
Login now