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