##// END OF EJS Templates
Enumerations can now have custom fields defined on them. #4077...
Eric Davis -
r2831:ac4937a76755
parent child
Show More
@@ -0,0 +1,23
1 # redMine - project management software
2 # Copyright (C) 2006 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 DocumentCategoryCustomField < CustomField
19 def type_name
20 :enumeration_doc_categories
21 end
22 end
23
@@ -0,0 +1,23
1 # redMine - project management software
2 # Copyright (C) 2006 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 IssuePriorityCustomField < CustomField
19 def type_name
20 :enumeration_issue_priorities
21 end
22 end
23
@@ -0,0 +1,23
1 # redMine - project management software
2 # Copyright (C) 2006 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 TimeEntryActivityCustomField < CustomField
19 def type_name
20 :enumeration_time_entry_activities
21 end
22 end
23
@@ -1,84 +1,87
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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 EnumerationsController < ApplicationController
18 class EnumerationsController < ApplicationController
19 before_filter :require_admin
19 before_filter :require_admin
20
20
21 helper :custom_fields
22 include CustomFieldsHelper
23
21 def index
24 def index
22 list
25 list
23 render :action => 'list'
26 render :action => 'list'
24 end
27 end
25
28
26 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
29 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
27 verify :method => :post, :only => [ :destroy, :create, :update ],
30 verify :method => :post, :only => [ :destroy, :create, :update ],
28 :redirect_to => { :action => :list }
31 :redirect_to => { :action => :list }
29
32
30 def list
33 def list
31 end
34 end
32
35
33 def new
36 def new
34 begin
37 begin
35 @enumeration = params[:type].constantize.new
38 @enumeration = params[:type].constantize.new
36 rescue NameError
39 rescue NameError
37 @enumeration = Enumeration.new
40 @enumeration = Enumeration.new
38 end
41 end
39 end
42 end
40
43
41 def create
44 def create
42 @enumeration = Enumeration.new(params[:enumeration])
45 @enumeration = Enumeration.new(params[:enumeration])
43 @enumeration.type = params[:enumeration][:type]
46 @enumeration.type = params[:enumeration][:type]
44 if @enumeration.save
47 if @enumeration.save
45 flash[:notice] = l(:notice_successful_create)
48 flash[:notice] = l(:notice_successful_create)
46 redirect_to :action => 'list', :type => @enumeration.type
49 redirect_to :action => 'list', :type => @enumeration.type
47 else
50 else
48 render :action => 'new'
51 render :action => 'new'
49 end
52 end
50 end
53 end
51
54
52 def edit
55 def edit
53 @enumeration = Enumeration.find(params[:id])
56 @enumeration = Enumeration.find(params[:id])
54 end
57 end
55
58
56 def update
59 def update
57 @enumeration = Enumeration.find(params[:id])
60 @enumeration = Enumeration.find(params[:id])
58 @enumeration.type = params[:enumeration][:type] if params[:enumeration][:type]
61 @enumeration.type = params[:enumeration][:type] if params[:enumeration][:type]
59 if @enumeration.update_attributes(params[:enumeration])
62 if @enumeration.update_attributes(params[:enumeration])
60 flash[:notice] = l(:notice_successful_update)
63 flash[:notice] = l(:notice_successful_update)
61 redirect_to :action => 'list', :type => @enumeration.type
64 redirect_to :action => 'list', :type => @enumeration.type
62 else
65 else
63 render :action => 'edit'
66 render :action => 'edit'
64 end
67 end
65 end
68 end
66
69
67 def destroy
70 def destroy
68 @enumeration = Enumeration.find(params[:id])
71 @enumeration = Enumeration.find(params[:id])
69 if !@enumeration.in_use?
72 if !@enumeration.in_use?
70 # No associated objects
73 # No associated objects
71 @enumeration.destroy
74 @enumeration.destroy
72 redirect_to :action => 'index'
75 redirect_to :action => 'index'
73 elsif params[:reassign_to_id]
76 elsif params[:reassign_to_id]
74 if reassign_to = Enumeration.find_by_type_and_id(@enumeration.type, params[:reassign_to_id])
77 if reassign_to = Enumeration.find_by_type_and_id(@enumeration.type, params[:reassign_to_id])
75 @enumeration.destroy(reassign_to)
78 @enumeration.destroy(reassign_to)
76 redirect_to :action => 'index'
79 redirect_to :action => 'index'
77 end
80 end
78 end
81 end
79 @enumerations = Enumeration.find(:all, :conditions => ['type = (?)', @enumeration.type]) - [@enumeration]
82 @enumerations = Enumeration.find(:all, :conditions => ['type = (?)', @enumeration.type]) - [@enumeration]
80 #rescue
83 #rescue
81 # flash[:error] = 'Unable to delete enumeration'
84 # flash[:error] = 'Unable to delete enumeration'
82 # redirect_to :action => 'index'
85 # redirect_to :action => 'index'
83 end
86 end
84 end
87 end
@@ -1,89 +1,92
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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 module CustomFieldsHelper
18 module CustomFieldsHelper
19
19
20 def custom_fields_tabs
20 def custom_fields_tabs
21 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
21 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
22 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
22 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
23 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
23 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
24 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
24 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
25 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural}
25 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
26 {:name => 'TimeEntryActivityCustomField', :label => TimeEntryActivity::OptionName},
27 {:name => 'IssuePriorityCustomField', :label => IssuePriority::OptionName},
28 {:name => 'DocumentCategoryCustomField', :label => DocumentCategory::OptionName}
26 ]
29 ]
27 end
30 end
28
31
29 # Return custom field html tag corresponding to its format
32 # Return custom field html tag corresponding to its format
30 def custom_field_tag(name, custom_value)
33 def custom_field_tag(name, custom_value)
31 custom_field = custom_value.custom_field
34 custom_field = custom_value.custom_field
32 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
35 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
33 field_id = "#{name}_custom_field_values_#{custom_field.id}"
36 field_id = "#{name}_custom_field_values_#{custom_field.id}"
34
37
35 case custom_field.field_format
38 case custom_field.field_format
36 when "date"
39 when "date"
37 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
40 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
38 calendar_for(field_id)
41 calendar_for(field_id)
39 when "text"
42 when "text"
40 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
43 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
41 when "bool"
44 when "bool"
42 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
45 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
43 when "list"
46 when "list"
44 blank_option = custom_field.is_required? ?
47 blank_option = custom_field.is_required? ?
45 (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
48 (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
46 '<option></option>'
49 '<option></option>'
47 select_tag(field_name, blank_option + options_for_select(custom_field.possible_values, custom_value.value), :id => field_id)
50 select_tag(field_name, blank_option + options_for_select(custom_field.possible_values, custom_value.value), :id => field_id)
48 else
51 else
49 text_field_tag(field_name, custom_value.value, :id => field_id)
52 text_field_tag(field_name, custom_value.value, :id => field_id)
50 end
53 end
51 end
54 end
52
55
53 # Return custom field label tag
56 # Return custom field label tag
54 def custom_field_label_tag(name, custom_value)
57 def custom_field_label_tag(name, custom_value)
55 content_tag "label", custom_value.custom_field.name +
58 content_tag "label", custom_value.custom_field.name +
56 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""),
59 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""),
57 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
60 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
58 :class => (custom_value.errors.empty? ? nil : "error" )
61 :class => (custom_value.errors.empty? ? nil : "error" )
59 end
62 end
60
63
61 # Return custom field tag with its label tag
64 # Return custom field tag with its label tag
62 def custom_field_tag_with_label(name, custom_value)
65 def custom_field_tag_with_label(name, custom_value)
63 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
66 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
64 end
67 end
65
68
66 # Return a string used to display a custom value
69 # Return a string used to display a custom value
67 def show_value(custom_value)
70 def show_value(custom_value)
68 return "" unless custom_value
71 return "" unless custom_value
69 format_value(custom_value.value, custom_value.custom_field.field_format)
72 format_value(custom_value.value, custom_value.custom_field.field_format)
70 end
73 end
71
74
72 # Return a string used to display a custom value
75 # Return a string used to display a custom value
73 def format_value(value, field_format)
76 def format_value(value, field_format)
74 return "" unless value && !value.empty?
77 return "" unless value && !value.empty?
75 case field_format
78 case field_format
76 when "date"
79 when "date"
77 begin; format_date(value.to_date); rescue; value end
80 begin; format_date(value.to_date); rescue; value end
78 when "bool"
81 when "bool"
79 l(value == "1" ? :general_text_Yes : :general_text_No)
82 l(value == "1" ? :general_text_Yes : :general_text_No)
80 else
83 else
81 value
84 value
82 end
85 end
83 end
86 end
84
87
85 # Return an array of custom field formats which can be used in select_tag
88 # Return an array of custom field formats which can be used in select_tag
86 def custom_field_formats_for_select
89 def custom_field_formats_for_select
87 CustomField::FIELD_FORMATS.sort {|a,b| a[1][:order]<=>b[1][:order]}.collect { |k| [ l(k[1][:name]), k[0] ] }
90 CustomField::FIELD_FORMATS.sort {|a,b| a[1][:order]<=>b[1][:order]}.collect { |k| [ l(k[1][:name]), k[0] ] }
88 end
91 end
89 end
92 end
@@ -1,131 +1,132
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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 Enumeration < ActiveRecord::Base
18 class Enumeration < ActiveRecord::Base
19 acts_as_list :scope => 'type = \'#{type}\''
19 acts_as_list :scope => 'type = \'#{type}\''
20 acts_as_customizable
20
21
21 before_destroy :check_integrity
22 before_destroy :check_integrity
22
23
23 validates_presence_of :name
24 validates_presence_of :name
24 validates_uniqueness_of :name, :scope => [:type]
25 validates_uniqueness_of :name, :scope => [:type]
25 validates_length_of :name, :maximum => 30
26 validates_length_of :name, :maximum => 30
26
27
27 # Backwards compatiblity named_scopes.
28 # Backwards compatiblity named_scopes.
28 # Can be removed post-0.9
29 # Can be removed post-0.9
29 named_scope :priorities, :conditions => { :type => "IssuePriority" }, :order => 'position' do
30 named_scope :priorities, :conditions => { :type => "IssuePriority" }, :order => 'position' do
30 ActiveSupport::Deprecation.warn("Enumeration#priorities is deprecated, use the IssuePriority class. (#{Redmine::Info.issue(3007)})")
31 ActiveSupport::Deprecation.warn("Enumeration#priorities is deprecated, use the IssuePriority class. (#{Redmine::Info.issue(3007)})")
31 def default
32 def default
32 find(:first, :conditions => { :is_default => true })
33 find(:first, :conditions => { :is_default => true })
33 end
34 end
34 end
35 end
35
36
36 named_scope :document_categories, :conditions => { :type => "DocumentCategory" }, :order => 'position' do
37 named_scope :document_categories, :conditions => { :type => "DocumentCategory" }, :order => 'position' do
37 ActiveSupport::Deprecation.warn("Enumeration#document_categories is deprecated, use the DocumentCategories class. (#{Redmine::Info.issue(3007)})")
38 ActiveSupport::Deprecation.warn("Enumeration#document_categories is deprecated, use the DocumentCategories class. (#{Redmine::Info.issue(3007)})")
38 def default
39 def default
39 find(:first, :conditions => { :is_default => true })
40 find(:first, :conditions => { :is_default => true })
40 end
41 end
41 end
42 end
42
43
43 named_scope :activities, :conditions => { :type => "TimeEntryActivity" }, :order => 'position' do
44 named_scope :activities, :conditions => { :type => "TimeEntryActivity" }, :order => 'position' do
44 ActiveSupport::Deprecation.warn("Enumeration#activities is deprecated, use the TimeEntryActivity class. (#{Redmine::Info.issue(3007)})")
45 ActiveSupport::Deprecation.warn("Enumeration#activities is deprecated, use the TimeEntryActivity class. (#{Redmine::Info.issue(3007)})")
45 def default
46 def default
46 find(:first, :conditions => { :is_default => true })
47 find(:first, :conditions => { :is_default => true })
47 end
48 end
48 end
49 end
49
50
50 named_scope :values, lambda {|type| { :conditions => { :type => type }, :order => 'position' } } do
51 named_scope :values, lambda {|type| { :conditions => { :type => type }, :order => 'position' } } do
51 def default
52 def default
52 find(:first, :conditions => { :is_default => true })
53 find(:first, :conditions => { :is_default => true })
53 end
54 end
54 end
55 end
55
56
56 named_scope :all, :order => 'position'
57 named_scope :all, :order => 'position'
57
58
58 def self.default
59 def self.default
59 # Creates a fake default scope so Enumeration.default will check
60 # Creates a fake default scope so Enumeration.default will check
60 # it's type. STI subclasses will automatically add their own
61 # it's type. STI subclasses will automatically add their own
61 # types to the finder.
62 # types to the finder.
62 if self.descends_from_active_record?
63 if self.descends_from_active_record?
63 find(:first, :conditions => { :is_default => true, :type => 'Enumeration' })
64 find(:first, :conditions => { :is_default => true, :type => 'Enumeration' })
64 else
65 else
65 # STI classes are
66 # STI classes are
66 find(:first, :conditions => { :is_default => true })
67 find(:first, :conditions => { :is_default => true })
67 end
68 end
68 end
69 end
69
70
70 # Overloaded on concrete classes
71 # Overloaded on concrete classes
71 def option_name
72 def option_name
72 nil
73 nil
73 end
74 end
74
75
75 # Backwards compatiblity. Can be removed post-0.9
76 # Backwards compatiblity. Can be removed post-0.9
76 def opt
77 def opt
77 ActiveSupport::Deprecation.warn("Enumeration#opt is deprecated, use the STI classes now. (#{Redmine::Info.issue(3007)})")
78 ActiveSupport::Deprecation.warn("Enumeration#opt is deprecated, use the STI classes now. (#{Redmine::Info.issue(3007)})")
78 return OptName
79 return OptName
79 end
80 end
80
81
81 def before_save
82 def before_save
82 if is_default? && is_default_changed?
83 if is_default? && is_default_changed?
83 Enumeration.update_all("is_default = #{connection.quoted_false}", {:type => type})
84 Enumeration.update_all("is_default = #{connection.quoted_false}", {:type => type})
84 end
85 end
85 end
86 end
86
87
87 # Overloaded on concrete classes
88 # Overloaded on concrete classes
88 def objects_count
89 def objects_count
89 0
90 0
90 end
91 end
91
92
92 def in_use?
93 def in_use?
93 self.objects_count != 0
94 self.objects_count != 0
94 end
95 end
95
96
96 alias :destroy_without_reassign :destroy
97 alias :destroy_without_reassign :destroy
97
98
98 # Destroy the enumeration
99 # Destroy the enumeration
99 # If a enumeration is specified, objects are reassigned
100 # If a enumeration is specified, objects are reassigned
100 def destroy(reassign_to = nil)
101 def destroy(reassign_to = nil)
101 if reassign_to && reassign_to.is_a?(Enumeration)
102 if reassign_to && reassign_to.is_a?(Enumeration)
102 self.transfer_relations(reassign_to)
103 self.transfer_relations(reassign_to)
103 end
104 end
104 destroy_without_reassign
105 destroy_without_reassign
105 end
106 end
106
107
107 def <=>(enumeration)
108 def <=>(enumeration)
108 position <=> enumeration.position
109 position <=> enumeration.position
109 end
110 end
110
111
111 def to_s; name end
112 def to_s; name end
112
113
113 # Returns the Subclasses of Enumeration. Each Subclass needs to be
114 # Returns the Subclasses of Enumeration. Each Subclass needs to be
114 # required in development mode.
115 # required in development mode.
115 #
116 #
116 # Note: subclasses is protected in ActiveRecord
117 # Note: subclasses is protected in ActiveRecord
117 def self.get_subclasses
118 def self.get_subclasses
118 @@subclasses[Enumeration]
119 @@subclasses[Enumeration]
119 end
120 end
120
121
121 private
122 private
122 def check_integrity
123 def check_integrity
123 raise "Can't delete enumeration" if self.in_use?
124 raise "Can't delete enumeration" if self.in_use?
124 end
125 end
125
126
126 end
127 end
127
128
128 # Force load the subclasses in development mode
129 # Force load the subclasses in development mode
129 require_dependency 'time_entry_activity'
130 require_dependency 'time_entry_activity'
130 require_dependency 'document_category'
131 require_dependency 'document_category'
131 require_dependency 'issue_priority'
132 require_dependency 'issue_priority'
@@ -1,100 +1,103
1 <%= error_messages_for 'custom_field' %>
1 <%= error_messages_for 'custom_field' %>
2
2
3 <script type="text/javascript">
3 <script type="text/javascript">
4 //<![CDATA[
4 //<![CDATA[
5 function toggle_custom_field_format() {
5 function toggle_custom_field_format() {
6 format = $("custom_field_field_format");
6 format = $("custom_field_field_format");
7 p_length = $("custom_field_min_length");
7 p_length = $("custom_field_min_length");
8 p_regexp = $("custom_field_regexp");
8 p_regexp = $("custom_field_regexp");
9 p_values = $("custom_field_possible_values");
9 p_values = $("custom_field_possible_values");
10 p_searchable = $("custom_field_searchable");
10 p_searchable = $("custom_field_searchable");
11 p_default = $("custom_field_default_value");
11 p_default = $("custom_field_default_value");
12
12
13 p_default.setAttribute('type','text');
13 p_default.setAttribute('type','text');
14 Element.show(p_default.parentNode);
14 Element.show(p_default.parentNode);
15
15
16 switch (format.value) {
16 switch (format.value) {
17 case "list":
17 case "list":
18 Element.hide(p_length.parentNode);
18 Element.hide(p_length.parentNode);
19 Element.hide(p_regexp.parentNode);
19 Element.hide(p_regexp.parentNode);
20 if (p_searchable) Element.show(p_searchable.parentNode);
20 if (p_searchable) Element.show(p_searchable.parentNode);
21 Element.show(p_values);
21 Element.show(p_values);
22 break;
22 break;
23 case "bool":
23 case "bool":
24 p_default.setAttribute('type','checkbox');
24 p_default.setAttribute('type','checkbox');
25 Element.hide(p_length.parentNode);
25 Element.hide(p_length.parentNode);
26 Element.hide(p_regexp.parentNode);
26 Element.hide(p_regexp.parentNode);
27 if (p_searchable) Element.hide(p_searchable.parentNode);
27 if (p_searchable) Element.hide(p_searchable.parentNode);
28 Element.hide(p_values);
28 Element.hide(p_values);
29 break;
29 break;
30 case "date":
30 case "date":
31 Element.hide(p_length.parentNode);
31 Element.hide(p_length.parentNode);
32 Element.hide(p_regexp.parentNode);
32 Element.hide(p_regexp.parentNode);
33 if (p_searchable) Element.hide(p_searchable.parentNode);
33 if (p_searchable) Element.hide(p_searchable.parentNode);
34 Element.hide(p_values);
34 Element.hide(p_values);
35 break;
35 break;
36 case "float":
36 case "float":
37 case "int":
37 case "int":
38 Element.show(p_length.parentNode);
38 Element.show(p_length.parentNode);
39 Element.show(p_regexp.parentNode);
39 Element.show(p_regexp.parentNode);
40 if (p_searchable) Element.hide(p_searchable.parentNode);
40 if (p_searchable) Element.hide(p_searchable.parentNode);
41 Element.hide(p_values);
41 Element.hide(p_values);
42 break;
42 break;
43 default:
43 default:
44 Element.show(p_length.parentNode);
44 Element.show(p_length.parentNode);
45 Element.show(p_regexp.parentNode);
45 Element.show(p_regexp.parentNode);
46 if (p_searchable) Element.show(p_searchable.parentNode);
46 if (p_searchable) Element.show(p_searchable.parentNode);
47 Element.hide(p_values);
47 Element.hide(p_values);
48 break;
48 break;
49 }
49 }
50 }
50 }
51
51
52 //]]>
52 //]]>
53 </script>
53 </script>
54
54
55 <div class="box">
55 <div class="box">
56 <p><%= f.text_field :name, :required => true %></p>
56 <p><%= f.text_field :name, :required => true %></p>
57 <p><%= f.select :field_format, custom_field_formats_for_select, {}, :onchange => "toggle_custom_field_format();",
57 <p><%= f.select :field_format, custom_field_formats_for_select, {}, :onchange => "toggle_custom_field_format();",
58 :disabled => !@custom_field.new_record? %></p>
58 :disabled => !@custom_field.new_record? %></p>
59 <p><label for="custom_field_min_length"><%=l(:label_min_max_length)%></label>
59 <p><label for="custom_field_min_length"><%=l(:label_min_max_length)%></label>
60 <%= f.text_field :min_length, :size => 5, :no_label => true %> -
60 <%= f.text_field :min_length, :size => 5, :no_label => true %> -
61 <%= f.text_field :max_length, :size => 5, :no_label => true %><br>(<%=l(:text_min_max_length_info)%>)</p>
61 <%= f.text_field :max_length, :size => 5, :no_label => true %><br>(<%=l(:text_min_max_length_info)%>)</p>
62 <p><%= f.text_field :regexp, :size => 50 %><br>(<%=l(:text_regexp_info)%>)</p>
62 <p><%= f.text_field :regexp, :size => 50 %><br>(<%=l(:text_regexp_info)%>)</p>
63 <p id="custom_field_possible_values"><%= f.text_area :possible_values, :value => @custom_field.possible_values.to_a.join("\n"),
63 <p id="custom_field_possible_values"><%= f.text_area :possible_values, :value => @custom_field.possible_values.to_a.join("\n"),
64 :cols => 20,
64 :cols => 20,
65 :rows => 15 %>
65 :rows => 15 %>
66 <br /><em><%= l(:text_custom_field_possible_values_info) %></em></p>
66 <br /><em><%= l(:text_custom_field_possible_values_info) %></em></p>
67 <p><%= @custom_field.field_format == 'bool' ? f.check_box(:default_value) : f.text_field(:default_value) %></p>
67 <p><%= @custom_field.field_format == 'bool' ? f.check_box(:default_value) : f.text_field(:default_value) %></p>
68 <%= call_hook(:view_custom_fields_form_upper_box, :custom_field => @custom_field, :form => f) %>
68 <%= call_hook(:view_custom_fields_form_upper_box, :custom_field => @custom_field, :form => f) %>
69 </div>
69 </div>
70
70
71 <div class="box">
71 <div class="box">
72 <% case @custom_field.class.name
72 <% case @custom_field.class.name
73 when "IssueCustomField" %>
73 when "IssueCustomField" %>
74
74
75 <fieldset><legend><%=l(:label_tracker_plural)%></legend>
75 <fieldset><legend><%=l(:label_tracker_plural)%></legend>
76 <% for tracker in @trackers %>
76 <% for tracker in @trackers %>
77 <%= check_box_tag "custom_field[tracker_ids][]", tracker.id, (@custom_field.trackers.include? tracker) %> <%= tracker.name %>
77 <%= check_box_tag "custom_field[tracker_ids][]", tracker.id, (@custom_field.trackers.include? tracker) %> <%= tracker.name %>
78 <% end %>
78 <% end %>
79 <%= hidden_field_tag "custom_field[tracker_ids][]", '' %>
79 <%= hidden_field_tag "custom_field[tracker_ids][]", '' %>
80 </fieldset>
80 </fieldset>
81 &nbsp;
81 &nbsp;
82 <p><%= f.check_box :is_required %></p>
82 <p><%= f.check_box :is_required %></p>
83 <p><%= f.check_box :is_for_all %></p>
83 <p><%= f.check_box :is_for_all %></p>
84 <p><%= f.check_box :is_filter %></p>
84 <p><%= f.check_box :is_filter %></p>
85 <p><%= f.check_box :searchable %></p>
85 <p><%= f.check_box :searchable %></p>
86
86
87 <% when "UserCustomField" %>
87 <% when "UserCustomField" %>
88 <p><%= f.check_box :is_required %></p>
88 <p><%= f.check_box :is_required %></p>
89 <p><%= f.check_box :editable %></p>
89 <p><%= f.check_box :editable %></p>
90
90
91 <% when "ProjectCustomField" %>
91 <% when "ProjectCustomField" %>
92 <p><%= f.check_box :is_required %></p>
92 <p><%= f.check_box :is_required %></p>
93
93
94 <% when "TimeEntryCustomField" %>
94 <% when "TimeEntryCustomField" %>
95 <p><%= f.check_box :is_required %></p>
95 <p><%= f.check_box :is_required %></p>
96
96
97 <% else %>
98 <p><%= f.check_box :is_required %></p>
99
97 <% end %>
100 <% end %>
98 <%= call_hook(:"view_custom_fields_form_#{@custom_field.type.to_s.underscore}", :custom_field => @custom_field, :form => f) %>
101 <%= call_hook(:"view_custom_fields_form_#{@custom_field.type.to_s.underscore}", :custom_field => @custom_field, :form => f) %>
99 </div>
102 </div>
100 <%= javascript_tag "toggle_custom_field_format();" %>
103 <%= javascript_tag "toggle_custom_field_format();" %>
@@ -1,12 +1,16
1 <%= error_messages_for 'enumeration' %>
1 <%= error_messages_for 'enumeration' %>
2 <div class="box">
2 <div class="box">
3 <!--[form:optvalue]-->
3 <!--[form:optvalue]-->
4 <%= hidden_field 'enumeration', 'type' %>
4 <%= hidden_field 'enumeration', 'type' %>
5
5
6 <p><label for="enumeration_name"><%=l(:field_name)%></label>
6 <p><label for="enumeration_name"><%=l(:field_name)%></label>
7 <%= text_field 'enumeration', 'name' %></p>
7 <%= text_field 'enumeration', 'name' %></p>
8
8
9 <p><label for="enumeration_is_default"><%=l(:field_is_default)%></label>
9 <p><label for="enumeration_is_default"><%=l(:field_is_default)%></label>
10 <%= check_box 'enumeration', 'is_default' %></p>
10 <%= check_box 'enumeration', 'is_default' %></p>
11 <!--[eoform:optvalue]-->
11 <!--[eoform:optvalue]-->
12
13 <% @enumeration.custom_field_values.each do |value| %>
14 <p><%= custom_field_tag_with_label :enumeration, value %></p>
15 <% end %>
12 </div> No newline at end of file
16 </div>
@@ -1,90 +1,103
1 ---
1 ---
2 custom_fields_001:
2 custom_fields_001:
3 name: Database
3 name: Database
4 min_length: 0
4 min_length: 0
5 regexp: ""
5 regexp: ""
6 is_for_all: true
6 is_for_all: true
7 is_filter: true
7 is_filter: true
8 type: IssueCustomField
8 type: IssueCustomField
9 max_length: 0
9 max_length: 0
10 possible_values:
10 possible_values:
11 - MySQL
11 - MySQL
12 - PostgreSQL
12 - PostgreSQL
13 - Oracle
13 - Oracle
14 id: 1
14 id: 1
15 is_required: false
15 is_required: false
16 field_format: list
16 field_format: list
17 default_value: ""
17 default_value: ""
18 editable: true
18 editable: true
19 custom_fields_002:
19 custom_fields_002:
20 name: Searchable field
20 name: Searchable field
21 min_length: 1
21 min_length: 1
22 regexp: ""
22 regexp: ""
23 is_for_all: true
23 is_for_all: true
24 type: IssueCustomField
24 type: IssueCustomField
25 max_length: 100
25 max_length: 100
26 possible_values: ""
26 possible_values: ""
27 id: 2
27 id: 2
28 is_required: false
28 is_required: false
29 field_format: string
29 field_format: string
30 searchable: true
30 searchable: true
31 default_value: "Default string"
31 default_value: "Default string"
32 editable: true
32 editable: true
33 custom_fields_003:
33 custom_fields_003:
34 name: Development status
34 name: Development status
35 min_length: 0
35 min_length: 0
36 regexp: ""
36 regexp: ""
37 is_for_all: false
37 is_for_all: false
38 is_filter: true
38 is_filter: true
39 type: ProjectCustomField
39 type: ProjectCustomField
40 max_length: 0
40 max_length: 0
41 possible_values:
41 possible_values:
42 - Stable
42 - Stable
43 - Beta
43 - Beta
44 - Alpha
44 - Alpha
45 - Planning
45 - Planning
46 id: 3
46 id: 3
47 is_required: true
47 is_required: true
48 field_format: list
48 field_format: list
49 default_value: ""
49 default_value: ""
50 editable: true
50 editable: true
51 custom_fields_004:
51 custom_fields_004:
52 name: Phone number
52 name: Phone number
53 min_length: 0
53 min_length: 0
54 regexp: ""
54 regexp: ""
55 is_for_all: false
55 is_for_all: false
56 type: UserCustomField
56 type: UserCustomField
57 max_length: 0
57 max_length: 0
58 possible_values: ""
58 possible_values: ""
59 id: 4
59 id: 4
60 is_required: false
60 is_required: false
61 field_format: string
61 field_format: string
62 default_value: ""
62 default_value: ""
63 editable: true
63 editable: true
64 custom_fields_005:
64 custom_fields_005:
65 name: Money
65 name: Money
66 min_length: 0
66 min_length: 0
67 regexp: ""
67 regexp: ""
68 is_for_all: false
68 is_for_all: false
69 type: UserCustomField
69 type: UserCustomField
70 max_length: 0
70 max_length: 0
71 possible_values: ""
71 possible_values: ""
72 id: 5
72 id: 5
73 is_required: false
73 is_required: false
74 field_format: float
74 field_format: float
75 default_value: ""
75 default_value: ""
76 editable: true
76 editable: true
77 custom_fields_006:
77 custom_fields_006:
78 name: Float field
78 name: Float field
79 min_length: 0
79 min_length: 0
80 regexp: ""
80 regexp: ""
81 is_for_all: true
81 is_for_all: true
82 type: IssueCustomField
82 type: IssueCustomField
83 max_length: 0
83 max_length: 0
84 possible_values: ""
84 possible_values: ""
85 id: 6
85 id: 6
86 is_required: false
86 is_required: false
87 field_format: float
87 field_format: float
88 default_value: ""
88 default_value: ""
89 editable: true
89 editable: true
90 No newline at end of file
90 custom_fields_007:
91 name: Billable
92 min_length: 0
93 regexp: ""
94 is_for_all: false
95 is_filter: true
96 type: TimeEntryActivityCustomField
97 max_length: 0
98 possible_values: ""
99 id: 7
100 is_required: false
101 field_format: bool
102 default_value: ""
103 editable: true
@@ -1,86 +1,91
1 ---
1 ---
2 custom_values_006:
2 custom_values_006:
3 customized_type: Issue
3 customized_type: Issue
4 custom_field_id: 2
4 custom_field_id: 2
5 customized_id: 3
5 customized_id: 3
6 id: 6
6 id: 6
7 value: "125"
7 value: "125"
8 custom_values_007:
8 custom_values_007:
9 customized_type: Project
9 customized_type: Project
10 custom_field_id: 3
10 custom_field_id: 3
11 customized_id: 1
11 customized_id: 1
12 id: 7
12 id: 7
13 value: Stable
13 value: Stable
14 custom_values_001:
14 custom_values_001:
15 customized_type: Principal
15 customized_type: Principal
16 custom_field_id: 4
16 custom_field_id: 4
17 customized_id: 3
17 customized_id: 3
18 id: 1
18 id: 1
19 value: ""
19 value: ""
20 custom_values_002:
20 custom_values_002:
21 customized_type: Principal
21 customized_type: Principal
22 custom_field_id: 4
22 custom_field_id: 4
23 customized_id: 4
23 customized_id: 4
24 id: 2
24 id: 2
25 value: 01 23 45 67 89
25 value: 01 23 45 67 89
26 custom_values_003:
26 custom_values_003:
27 customized_type: Principal
27 customized_type: Principal
28 custom_field_id: 4
28 custom_field_id: 4
29 customized_id: 2
29 customized_id: 2
30 id: 3
30 id: 3
31 value: ""
31 value: ""
32 custom_values_004:
32 custom_values_004:
33 customized_type: Issue
33 customized_type: Issue
34 custom_field_id: 2
34 custom_field_id: 2
35 customized_id: 1
35 customized_id: 1
36 id: 4
36 id: 4
37 value: "125"
37 value: "125"
38 custom_values_005:
38 custom_values_005:
39 customized_type: Issue
39 customized_type: Issue
40 custom_field_id: 2
40 custom_field_id: 2
41 customized_id: 2
41 customized_id: 2
42 id: 5
42 id: 5
43 value: ""
43 value: ""
44 custom_values_008:
44 custom_values_008:
45 customized_type: Issue
45 customized_type: Issue
46 custom_field_id: 1
46 custom_field_id: 1
47 customized_id: 3
47 customized_id: 3
48 id: 8
48 id: 8
49 value: "MySQL"
49 value: "MySQL"
50 custom_values_009:
50 custom_values_009:
51 customized_type: Issue
51 customized_type: Issue
52 custom_field_id: 2
52 custom_field_id: 2
53 customized_id: 3
53 customized_id: 3
54 id: 9
54 id: 9
55 value: "this is a stringforcustomfield search"
55 value: "this is a stringforcustomfield search"
56 custom_values_010:
56 custom_values_010:
57 customized_type: Issue
57 customized_type: Issue
58 custom_field_id: 6
58 custom_field_id: 6
59 customized_id: 1
59 customized_id: 1
60 id: 10
60 id: 10
61 value: "2.1"
61 value: "2.1"
62 custom_values_011:
62 custom_values_011:
63 customized_type: Issue
63 customized_type: Issue
64 custom_field_id: 6
64 custom_field_id: 6
65 customized_id: 2
65 customized_id: 2
66 id: 11
66 id: 11
67 value: "2.05"
67 value: "2.05"
68 custom_values_012:
68 custom_values_012:
69 customized_type: Issue
69 customized_type: Issue
70 custom_field_id: 6
70 custom_field_id: 6
71 customized_id: 3
71 customized_id: 3
72 id: 12
72 id: 12
73 value: "11.65"
73 value: "11.65"
74 custom_values_013:
74 custom_values_013:
75 customized_type: Issue
75 customized_type: Issue
76 custom_field_id: 6
76 custom_field_id: 6
77 customized_id: 7
77 customized_id: 7
78 id: 13
78 id: 13
79 value: ""
79 value: ""
80 custom_values_014:
80 custom_values_014:
81 customized_type: Issue
81 customized_type: Issue
82 custom_field_id: 6
82 custom_field_id: 6
83 customized_id: 5
83 customized_id: 5
84 id: 14
84 id: 14
85 value: "-7.6"
85 value: "-7.6"
86 No newline at end of file
86 custom_values_015:
87 customized_type: TimeEntryActivity
88 custom_field_id: 7
89 customized_id: 10
90 id: 15
91 value: true
@@ -1,84 +1,89
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class EnumerationTest < ActiveSupport::TestCase
20 class EnumerationTest < ActiveSupport::TestCase
21 fixtures :enumerations, :issues
21 fixtures :enumerations, :issues, :custom_fields, :custom_values
22
22
23 def setup
23 def setup
24 end
24 end
25
25
26 def test_objects_count
26 def test_objects_count
27 # low priority
27 # low priority
28 assert_equal 5, Enumeration.find(4).objects_count
28 assert_equal 5, Enumeration.find(4).objects_count
29 # urgent
29 # urgent
30 assert_equal 0, Enumeration.find(7).objects_count
30 assert_equal 0, Enumeration.find(7).objects_count
31 end
31 end
32
32
33 def test_in_use
33 def test_in_use
34 # low priority
34 # low priority
35 assert Enumeration.find(4).in_use?
35 assert Enumeration.find(4).in_use?
36 # urgent
36 # urgent
37 assert !Enumeration.find(7).in_use?
37 assert !Enumeration.find(7).in_use?
38 end
38 end
39
39
40 def test_default
40 def test_default
41 e = Enumeration.default
41 e = Enumeration.default
42 assert e.is_a?(Enumeration)
42 assert e.is_a?(Enumeration)
43 assert e.is_default?
43 assert e.is_default?
44 assert_equal 'Default Enumeration', e.name
44 assert_equal 'Default Enumeration', e.name
45 end
45 end
46
46
47 def test_create
47 def test_create
48 e = Enumeration.new(:name => 'Not default', :is_default => false)
48 e = Enumeration.new(:name => 'Not default', :is_default => false)
49 e.type = 'Enumeration'
49 e.type = 'Enumeration'
50 assert e.save
50 assert e.save
51 assert_equal 'Default Enumeration', Enumeration.default.name
51 assert_equal 'Default Enumeration', Enumeration.default.name
52 end
52 end
53
53
54 def test_create_as_default
54 def test_create_as_default
55 e = Enumeration.new(:name => 'Very urgent', :is_default => true)
55 e = Enumeration.new(:name => 'Very urgent', :is_default => true)
56 e.type = 'Enumeration'
56 e.type = 'Enumeration'
57 assert e.save
57 assert e.save
58 assert_equal e, Enumeration.default
58 assert_equal e, Enumeration.default
59 end
59 end
60
60
61 def test_update_default
61 def test_update_default
62 e = Enumeration.default
62 e = Enumeration.default
63 e.update_attributes(:name => 'Changed', :is_default => true)
63 e.update_attributes(:name => 'Changed', :is_default => true)
64 assert_equal e, Enumeration.default
64 assert_equal e, Enumeration.default
65 end
65 end
66
66
67 def test_update_default_to_non_default
67 def test_update_default_to_non_default
68 e = Enumeration.default
68 e = Enumeration.default
69 e.update_attributes(:name => 'Changed', :is_default => false)
69 e.update_attributes(:name => 'Changed', :is_default => false)
70 assert_nil Enumeration.default
70 assert_nil Enumeration.default
71 end
71 end
72
72
73 def test_change_default
73 def test_change_default
74 e = Enumeration.find_by_name('Default Enumeration')
74 e = Enumeration.find_by_name('Default Enumeration')
75 e.update_attributes(:name => 'Changed Enumeration', :is_default => true)
75 e.update_attributes(:name => 'Changed Enumeration', :is_default => true)
76 assert_equal e, Enumeration.default
76 assert_equal e, Enumeration.default
77 end
77 end
78
78
79 def test_destroy_with_reassign
79 def test_destroy_with_reassign
80 Enumeration.find(4).destroy(Enumeration.find(6))
80 Enumeration.find(4).destroy(Enumeration.find(6))
81 assert_nil Issue.find(:first, :conditions => {:priority_id => 4})
81 assert_nil Issue.find(:first, :conditions => {:priority_id => 4})
82 assert_equal 5, Enumeration.find(6).objects_count
82 assert_equal 5, Enumeration.find(6).objects_count
83 end
83 end
84
85 def test_should_be_customizable
86 assert Enumeration.included_modules.include?(Redmine::Acts::Customizable::InstanceMethods)
87 end
88
84 end
89 end
@@ -1,36 +1,85
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class TimeEntryActivityTest < ActiveSupport::TestCase
20 class TimeEntryActivityTest < ActiveSupport::TestCase
21 fixtures :enumerations, :time_entries
21 fixtures :enumerations, :time_entries
22
22
23 def test_should_be_an_enumeration
23 def test_should_be_an_enumeration
24 assert TimeEntryActivity.ancestors.include?(Enumeration)
24 assert TimeEntryActivity.ancestors.include?(Enumeration)
25 end
25 end
26
26
27 def test_objects_count
27 def test_objects_count
28 assert_equal 3, TimeEntryActivity.find_by_name("Design").objects_count
28 assert_equal 3, TimeEntryActivity.find_by_name("Design").objects_count
29 assert_equal 1, TimeEntryActivity.find_by_name("Development").objects_count
29 assert_equal 1, TimeEntryActivity.find_by_name("Development").objects_count
30 end
30 end
31
31
32 def test_option_name
32 def test_option_name
33 assert_equal :enumeration_activities, TimeEntryActivity.new.option_name
33 assert_equal :enumeration_activities, TimeEntryActivity.new.option_name
34 end
34 end
35
36 def test_create_with_custom_field
37 field = TimeEntryActivityCustomField.find_by_name('Billable')
38 e = TimeEntryActivity.new(:name => 'Custom Data')
39 e.custom_field_values = {field.id => "1"}
40 assert e.save
41
42 e.reload
43 assert_equal "1", e.custom_value_for(field).value
44 end
45
46 def test_create_without_required_custom_field_should_fail
47 field = TimeEntryActivityCustomField.find_by_name('Billable')
48 field.update_attribute(:is_required, true)
49
50 e = TimeEntryActivity.new(:name => 'Custom Data')
51 assert !e.save
52 assert_equal I18n.translate('activerecord.errors.messages.invalid'), e.errors.on(:custom_values)
53 end
54
55 def test_create_with_required_custom_field_should_succeed
56 field = TimeEntryActivityCustomField.find_by_name('Billable')
57 field.update_attribute(:is_required, true)
58
59 e = TimeEntryActivity.new(:name => 'Custom Data')
60 e.custom_field_values = {field.id => "1"}
61 assert e.save
62 end
63
64 def test_update_issue_with_required_custom_field_change
65 field = TimeEntryActivityCustomField.find_by_name('Billable')
66 field.update_attribute(:is_required, true)
67
68 e = TimeEntryActivity.find(10)
69 assert e.available_custom_fields.include?(field)
70 # No change to custom field, record can be saved
71 assert e.save
72 # Blanking custom field, save should fail
73 e.custom_field_values = {field.id => ""}
74 assert !e.save
75 assert e.errors.on(:custom_values)
76
77 # Update custom field to valid value, save should succeed
78 e.custom_field_values = {field.id => "0"}
79 assert e.save
80 e.reload
81 assert_equal "0", e.custom_value_for(field).value
82 end
83
35 end
84 end
36
85
General Comments 0
You need to be logged in to leave comments. Login now