Commit d8fbdca7 authored by Jean-Philippe Lang's avatar Jean-Philippe Lang Committed by Eric Davis

Adds User and Version custom field format that can be used to reference a…

Adds User and Version custom field format that can be used to reference a project member or version in custom fields (#2096).

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
parent 0bf7a604
...@@ -49,7 +49,7 @@ module CustomFieldsHelper ...@@ -49,7 +49,7 @@ module CustomFieldsHelper
blank_option = custom_field.is_required? ? blank_option = custom_field.is_required? ?
(custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') : (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
'<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 else
text_field_tag(field_name, custom_value.value, :id => field_id) text_field_tag(field_name, custom_value.value, :id => field_id)
end end
...@@ -83,7 +83,7 @@ module CustomFieldsHelper ...@@ -83,7 +83,7 @@ module CustomFieldsHelper
[l(:general_text_yes), '1'], [l(:general_text_yes), '1'],
[l(:general_text_no), '0']]), :id => field_id) [l(:general_text_no), '0']]), :id => field_id)
when "list" 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 else
text_field_tag(field_name, '', :id => field_id) text_field_tag(field_name, '', :id => field_id)
end end
...@@ -101,8 +101,8 @@ module CustomFieldsHelper ...@@ -101,8 +101,8 @@ module CustomFieldsHelper
end end
# Return an array of custom field formats which can be used in select_tag # Return an array of custom field formats which can be used in select_tag
def custom_field_formats_for_select def custom_field_formats_for_select(custom_field)
Redmine::CustomFieldFormat.as_select Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
end end
# Renders the custom_values in api views # Renders the custom_values in api views
......
# redMine - project management software # Redmine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006-2011 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
...@@ -48,6 +48,33 @@ class CustomField < ActiveRecord::Base ...@@ -48,6 +48,33 @@ class CustomField < ActiveRecord::Base
errors.add(:default_value, :invalid) unless v.valid? errors.add(:default_value, :invalid) unless v.valid?
end 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 # Makes possible_values accept a multiline string
def possible_values=(arg) def possible_values=(arg)
if arg.is_a?(Array) if arg.is_a?(Array)
...@@ -71,6 +98,8 @@ class CustomField < ActiveRecord::Base ...@@ -71,6 +98,8 @@ class CustomField < ActiveRecord::Base
casted = value.to_i casted = value.to_i
when 'float' when 'float'
casted = value.to_f casted = value.to_f
when 'user', 'version'
casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
end end
end end
casted casted
......
# Redmine - project management software # 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 # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
...@@ -636,6 +636,9 @@ class Query < ActiveRecord::Base ...@@ -636,6 +636,9 @@ class Query < ActiveRecord::Base
options = { :type => :date, :order => 20 } options = { :type => :date, :order => 20 }
when "bool" when "bool"
options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 } 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 else
options = { :type => :string, :order => 20 } options = { :type => :string, :order => 20 }
end end
......
...@@ -40,6 +40,14 @@ function toggle_custom_field_format() { ...@@ -40,6 +40,14 @@ function toggle_custom_field_format() {
if (p_searchable) Element.hide(p_searchable.parentNode); if (p_searchable) Element.hide(p_searchable.parentNode);
Element.hide(p_values.parentNode); Element.hide(p_values.parentNode);
break; 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: default:
Element.show(p_length.parentNode); Element.show(p_length.parentNode);
Element.show(p_regexp.parentNode); Element.show(p_regexp.parentNode);
...@@ -54,7 +62,7 @@ function toggle_custom_field_format() { ...@@ -54,7 +62,7 @@ function toggle_custom_field_format() {
<div class="box"> <div class="box">
<p><%= f.text_field :name, :required => true %></p> <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> :disabled => !@custom_field.new_record? %></p>
<p><label for="custom_field_min_length"><%=l(:label_min_max_length)%></label> <p><label for="custom_field_min_length"><%=l(:label_min_max_length)%></label>
<%= f.text_field :min_length, :size => 5, :no_label => true %> - <%= f.text_field :min_length, :size => 5, :no_label => true %> -
......
...@@ -41,6 +41,8 @@ Redmine::CustomFieldFormat.map do |fields| ...@@ -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('list', :label => :label_list, :order => 5)
fields.register Redmine::CustomFieldFormat.new('date', :label => :label_date, :order => 6) 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('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 end
# Permissions # Permissions
......
...@@ -22,12 +22,14 @@ module Redmine ...@@ -22,12 +22,14 @@ module Redmine
cattr_accessor :available cattr_accessor :available
@@available = {} @@available = {}
attr_accessor :name, :order, :label attr_accessor :name, :order, :label, :edit_as, :class_names
def initialize(name, options={}) def initialize(name, options={})
self.name = name self.name = name
self.label = options[:label] self.label = options[:label]
self.order = options[:order] self.order = options[:order]
self.edit_as = options[:edit_as] || name
self.class_names = options[:only]
end end
def format(value) def format(value)
...@@ -48,11 +50,10 @@ module Redmine ...@@ -48,11 +50,10 @@ module Redmine
} }
end end
# Allow displaying the edit type of another field_format ['user', 'version'].each do |name|
# define_method("format_as_#{name}") {|value|
# Example: display a custom field as a list return value.blank? ? "" : name.classify.constantize.find_by_id(value.to_i).to_s
def edit_as }
name
end end
class << self class << self
...@@ -79,8 +80,10 @@ module Redmine ...@@ -79,8 +80,10 @@ module Redmine
end end
# Return an array of custom field formats which can be used in select_tag # Return an array of custom field formats which can be used in select_tag
def as_select def as_select(class_name=nil)
@@available.values.sort {|a,b| 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 a.order <=> b.order
}.collect {|custom_field_format| }.collect {|custom_field_format|
[ l(custom_field_format.label), custom_field_format.name ] [ l(custom_field_format.label), custom_field_format.name ]
......
# Redmine - project management software # 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 # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
...@@ -31,6 +31,31 @@ class CustomFieldsControllerTest < ActionController::TestCase ...@@ -31,6 +31,31 @@ class CustomFieldsControllerTest < ActionController::TestCase
@request.session[:user_id] = 1 @request.session[:user_id] = 1
end 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 def test_post_new_list_custom_field
assert_difference 'CustomField.count' do assert_difference 'CustomField.count' do
post :new, :type => "IssueCustomField", post :new, :type => "IssueCustomField",
...@@ -53,9 +78,4 @@ class CustomFieldsControllerTest < ActionController::TestCase ...@@ -53,9 +78,4 @@ class CustomFieldsControllerTest < ActionController::TestCase
assert_equal ["0.1", "0.2"], field.possible_values assert_equal ["0.1", "0.2"], field.possible_values
assert_equal 1, field.trackers.size assert_equal 1, field.trackers.size
end end
def test_invalid_custom_field_class_should_redirect_to_list
get :new, :type => 'UnknownCustomField'
assert_redirected_to '/custom_fields'
end
end end
# redMine - project management software # 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 # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
...@@ -126,4 +126,75 @@ class IssuesTest < ActionController::IntegrationTest ...@@ -126,4 +126,75 @@ class IssuesTest < ActionController::IntegrationTest
:attributes => { :href => '/projects/ecookbook/issues?page=2' } :attributes => { :href => '/projects/ecookbook/issues?page=2' }
end 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 end
# 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
# redMine - project management software # 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 # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
...@@ -75,7 +75,7 @@ module Redmine ...@@ -75,7 +75,7 @@ module Redmine
end end
def custom_field_values 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 end
def visible_custom_field_values def visible_custom_field_values
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment