OSDN Git Service

Adds User and Version custom field format that can be used to reference a project...
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Fri, 1 Apr 2011 13:44:58 +0000 (13:44 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Fri, 1 Apr 2011 13:44:58 +0000 (13:44 +0000)
These new field formats are available for project, issue, version and time entry custom fields.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@5272 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/helpers/custom_fields_helper.rb
app/models/custom_field.rb
app/models/query.rb
app/views/custom_fields/_form.rhtml
lib/redmine.rb
lib/redmine/custom_field_format.rb
test/functional/custom_fields_controller_test.rb
test/integration/issues_test.rb
test/unit/custom_field_user_format_test.rb [new file with mode: 0644]
vendor/plugins/acts_as_customizable/lib/acts_as_customizable.rb

index efe31a4..36f665e 100644 (file)
@@ -49,7 +49,7 @@ module CustomFieldsHelper
       blank_option = custom_field.is_required? ?
                        (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') : 
                        '<option></option>'
-      select_tag(field_name, blank_option + options_for_select(custom_field.possible_values, custom_value.value), :id => field_id)
+      select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
     else
       text_field_tag(field_name, custom_value.value, :id => field_id)
     end
@@ -83,7 +83,7 @@ module CustomFieldsHelper
                                                    [l(:general_text_yes), '1'],
                                                    [l(:general_text_no), '0']]), :id => field_id)
       when "list"
-        select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values), :id => field_id)
+        select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values_options), :id => field_id)
       else
         text_field_tag(field_name, '', :id => field_id)
     end
@@ -101,8 +101,8 @@ module CustomFieldsHelper
   end
 
   # Return an array of custom field formats which can be used in select_tag
-  def custom_field_formats_for_select
-    Redmine::CustomFieldFormat.as_select
+  def custom_field_formats_for_select(custom_field)
+    Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
   end
   
   # Renders the custom_values in api views
index 3aacd46..8d7927a 100644 (file)
@@ -1,5 +1,5 @@
-# redMine - project management software
-# Copyright (C) 2006  Jean-Philippe Lang
+# Redmine - project management software
+# Copyright (C) 2006-2011  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -48,6 +48,33 @@ class CustomField < ActiveRecord::Base
     errors.add(:default_value, :invalid) unless v.valid?
   end
   
+  def possible_values_options(obj=nil)
+    case field_format
+    when 'user', 'version'
+      if obj.respond_to?(:project)
+        case field_format
+        when 'user'
+          obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
+        when 'version'
+          obj.project.versions.sort.collect {|u| [u.to_s, u.id.to_s]}
+        end
+      else
+        []
+      end
+    else
+      read_attribute :possible_values
+    end
+  end
+  
+  def possible_values(obj=nil)
+    case field_format
+    when 'user'
+      possible_values_options(obj).collect(&:last)
+    else
+      read_attribute :possible_values
+    end
+  end
+  
   # Makes possible_values accept a multiline string
   def possible_values=(arg)
     if arg.is_a?(Array)
@@ -71,6 +98,8 @@ class CustomField < ActiveRecord::Base
         casted = value.to_i
       when 'float'
         casted = value.to_f
+      when 'user', 'version'
+        casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
       end
     end
     casted
index 3b1b53c..6c4d1c7 100644 (file)
@@ -1,5 +1,5 @@
 # Redmine - project management software
-# Copyright (C) 2006-2008  Jean-Philippe Lang
+# Copyright (C) 2006-2011  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -636,6 +636,9 @@ class Query < ActiveRecord::Base
         options = { :type => :date, :order => 20 }
       when "bool"
         options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
+      when "user", "version"
+        next unless project
+        options = { :type => :list_optional, :values => field.possible_values_options(project), :order => 20}
       else
         options = { :type => :string, :order => 20 }
       end
index 8cbd903..2900af9 100644 (file)
@@ -40,6 +40,14 @@ function toggle_custom_field_format() {
       if (p_searchable) Element.hide(p_searchable.parentNode);
       Element.hide(p_values.parentNode);
       break;
+               case "user":
+    case "version":
+      Element.hide(p_length.parentNode);
+      Element.hide(p_regexp.parentNode);
+      if (p_searchable) Element.hide(p_searchable.parentNode);
+      Element.hide(p_values.parentNode);
+      Element.hide(p_default.parentNode);
+                 break;
     default:
       Element.show(p_length.parentNode);
       Element.show(p_regexp.parentNode);
@@ -54,7 +62,7 @@ function toggle_custom_field_format() {
 
 <div class="box">
 <p><%= f.text_field :name, :required => true %></p>
-<p><%= f.select :field_format, custom_field_formats_for_select, {}, :onchange => "toggle_custom_field_format();",
+<p><%= f.select :field_format, custom_field_formats_for_select(@custom_field), {}, :onchange => "toggle_custom_field_format();",
                                                                                                                                                                                                                                                                                :disabled => !@custom_field.new_record? %></p>
 <p><label for="custom_field_min_length"><%=l(:label_min_max_length)%></label>
    <%= f.text_field :min_length, :size => 5, :no_label => true %> - 
index 87efc72..631f109 100644 (file)
@@ -41,6 +41,8 @@ Redmine::CustomFieldFormat.map do |fields|
   fields.register Redmine::CustomFieldFormat.new('list', :label => :label_list, :order => 5)
   fields.register Redmine::CustomFieldFormat.new('date', :label => :label_date, :order => 6)
   fields.register Redmine::CustomFieldFormat.new('bool', :label => :label_boolean, :order => 7)
+  fields.register Redmine::CustomFieldFormat.new('user', :label => :label_user, :only => %w(Issue TimeEntry Version Project), :edit_as => 'list', :order => 8)
+  fields.register Redmine::CustomFieldFormat.new('version', :label => :label_version, :only => %w(Issue TimeEntry Version Project), :edit_as => 'list', :order => 9)
 end
 
 # Permissions
index 2f12397..7c6364f 100644 (file)
@@ -22,12 +22,14 @@ module Redmine
     cattr_accessor :available
     @@available = {}
 
-    attr_accessor :name, :order, :label
+    attr_accessor :name, :order, :label, :edit_as, :class_names
 
     def initialize(name, options={})
       self.name = name
       self.label = options[:label]
       self.order = options[:order]
+      self.edit_as = options[:edit_as] || name
+      self.class_names = options[:only]
     end
 
     def format(value)
@@ -47,12 +49,11 @@ module Redmine
         return value
       }
     end
-
-    # Allow displaying the edit type of another field_format
-    #
-    # Example: display a custom field as a list
-    def edit_as
-      name
+    
+    ['user', 'version'].each do |name|
+      define_method("format_as_#{name}") {|value|
+        return value.blank? ? "" : name.classify.constantize.find_by_id(value.to_i).to_s
+      }
     end
 
     class << self
@@ -79,8 +80,10 @@ module Redmine
       end
 
       # Return an array of custom field formats which can be used in select_tag
-      def as_select
-        @@available.values.sort {|a,b|
+      def as_select(class_name=nil)
+        fields = @@available.values
+        fields = fields.select {|field| field.class_names.nil? || field.class_names.include?(class_name)}
+        fields.sort {|a,b|
           a.order <=> b.order
         }.collect {|custom_field_format|
           [ l(custom_field_format.label), custom_field_format.name ]
index d1e251b..5e665ec 100644 (file)
@@ -1,5 +1,5 @@
 # Redmine - project management software
-# Copyright (C) 2006-2009  Jean-Philippe Lang
+# Copyright (C) 2006-2011  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -31,6 +31,31 @@ class CustomFieldsControllerTest < ActionController::TestCase
     @request.session[:user_id] = 1
   end
   
+  def test_get_new_issue_custom_field
+    get :new, :type => 'IssueCustomField'
+    assert_response :success
+    assert_template 'new'
+    assert_tag :select,
+      :attributes => {:name => 'custom_field[field_format]'},
+      :child => {
+        :tag => 'option',
+        :attributes => {:value => 'user'},
+        :content => 'User'
+      }
+    assert_tag :select,
+      :attributes => {:name => 'custom_field[field_format]'},
+      :child => {
+        :tag => 'option',
+        :attributes => {:value => 'version'},
+        :content => 'Version'
+      }
+  end
+  
+  def test_get_new_with_invalid_custom_field_class_should_redirect_to_list
+    get :new, :type => 'UnknownCustomField'
+    assert_redirected_to '/custom_fields'
+  end
+  
   def test_post_new_list_custom_field
     assert_difference 'CustomField.count' do
       post :new, :type => "IssueCustomField",
@@ -53,9 +78,4 @@ class CustomFieldsControllerTest < ActionController::TestCase
     assert_equal ["0.1", "0.2"], field.possible_values
     assert_equal 1, field.trackers.size
   end
-  
-  def test_invalid_custom_field_class_should_redirect_to_list
-    get :new, :type => 'UnknownCustomField'
-    assert_redirected_to '/custom_fields'
-  end
 end
index b6955d2..a640d1c 100644 (file)
@@ -1,5 +1,5 @@
-# redMine - project management software
-# Copyright (C) 2006-2008  Jean-Philippe Lang
+# Redmine - project management software
+# Copyright (C) 2006-2011  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -126,4 +126,75 @@ class IssuesTest < ActionController::IntegrationTest
                    :attributes => { :href => '/projects/ecookbook/issues?page=2' }
     
   end
+  
+  def test_issue_with_user_custom_field
+    @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :trackers => Tracker.all)
+    Role.anonymous.add_permission! :add_issues, :edit_issues
+    users = Project.find(1).users
+    tester = users.first
+    
+    # Issue form
+    get '/projects/ecookbook/issues/new'
+    assert_response :success
+    assert_tag :select,
+      :attributes => {:name => "issue[custom_field_values][#{@field.id}]"},
+      :children => {:count => (users.size + 1)}, # +1 for blank value
+      :child => {
+        :tag => 'option',
+        :attributes => {:value => tester.id.to_s},
+        :content => tester.name
+      }
+    
+    # Create issue
+    assert_difference 'Issue.count' do
+      post '/projects/ecookbook/issues', 
+        :issue => {
+          :tracker_id => '1',
+          :priority_id => '4',
+          :subject => 'Issue with user custom field',
+          :custom_field_values => {@field.id.to_s => users.first.id.to_s}
+        }
+    end
+    issue = Issue.first(:order => 'id DESC')
+    assert_response 302
+    
+    # Issue view
+    follow_redirect!
+    assert_tag :th,
+      :content => /Tester/,
+      :sibling => {
+        :tag => 'td',
+        :content => tester.name
+      }
+    assert_tag :select,
+      :attributes => {:name => "issue[custom_field_values][#{@field.id}]"},
+      :children => {:count => (users.size + 1)}, # +1 for blank value
+      :child => {
+        :tag => 'option',
+        :attributes => {:value => tester.id.to_s, :selected => 'selected'},
+        :content => tester.name
+      }
+    
+    # Update issue
+    new_tester = users[1]
+    assert_difference 'Journal.count' do
+      put "/issues/#{issue.id}",
+        :notes => 'Updating custom field',
+        :issue => {
+          :custom_field_values => {@field.id.to_s => new_tester.id.to_s}
+        }
+    end
+    assert_response 302
+    
+    # Issue view
+    follow_redirect!
+    assert_tag :content => 'Tester',
+      :ancestor => {:tag => 'ul', :attributes => {:class => /details/}},
+      :sibling => {
+        :content => tester.name,
+        :sibling => {
+          :content => new_tester.name
+        }
+      }
+  end
 end
diff --git a/test/unit/custom_field_user_format_test.rb b/test/unit/custom_field_user_format_test.rb
new file mode 100644 (file)
index 0000000..085e4c7
--- /dev/null
@@ -0,0 +1,65 @@
+# Redmine - project management software
+# Copyright (C) 2006-2011  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require File.expand_path('../../test_helper', __FILE__)
+
+class CustomFieldUserFormatTest < ActiveSupport::TestCase
+  fixtures :custom_fields, :projects, :members, :users, :member_roles, :trackers, :issues
+  
+  def setup
+    @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user')
+  end
+  
+  def test_possible_values_with_no_arguments
+    assert_equal [], @field.possible_values
+    assert_equal [], @field.possible_values(nil)
+  end
+  
+  def test_possible_values_with_project_resource
+    project = Project.find(1)
+    possible_values = @field.possible_values(project.issues.first)
+    assert possible_values.any?
+    assert_equal project.users.sort.collect(&:id).map(&:to_s), possible_values
+  end
+  
+  def test_possible_values_options_with_no_arguments
+    assert_equal [], @field.possible_values_options
+    assert_equal [], @field.possible_values_options(nil)
+  end
+  
+  def test_possible_values_options_with_project_resource
+    project = Project.find(1)
+    possible_values_options = @field.possible_values_options(project.issues.first)
+    assert possible_values_options.any?
+    assert_equal project.users.sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
+  end
+  
+  def test_cast_blank_value
+    assert_equal nil, @field.cast_value(nil)
+    assert_equal nil, @field.cast_value("")
+  end
+  
+  def test_cast_valid_value
+    user = @field.cast_value("2")
+    assert_kind_of User, user
+    assert_equal User.find(2), user
+  end
+  
+  def test_cast_invalid_value
+    assert_equal nil, @field.cast_value("187")
+  end
+end
index b99c38c..10ba123 100644 (file)
@@ -1,5 +1,5 @@
-# redMine - project management software
-# Copyright (C) 2006-2008  Jean-Philippe Lang
+# Redmine - project management software
+# Copyright (C) 2006-2011  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -75,7 +75,7 @@ module Redmine
         end
         
         def custom_field_values
-          @custom_field_values ||= available_custom_fields.collect { |x| custom_values.detect { |v| v.custom_field == x } || custom_values.build(:custom_field => x, :value => nil) }
+          @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) }
         end
         
         def visible_custom_field_values