diff --git a/app/helpers/workflows_helper.rb b/app/helpers/workflows_helper.rb index 21ee21d49..5b969503e 100644 --- a/app/helpers/workflows_helper.rb +++ b/app/helpers/workflows_helper.rb @@ -42,14 +42,19 @@ module WorkflowsHelper select_tag name, option_tags, {:multiple => multiple}.merge(options) end - def field_required?(field) - field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field) + def field_required?(field, trackers=[]) + if field == 'parent_issue_id' + subtasks = trackers.select {|t| t.subtask_only?} + subtasks.count == trackers.count + else + field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field) + end end - def field_permission_tag(permissions, status, field, roles) + def field_permission_tag(permissions, status, field, roles, trackers=[]) name = field.is_a?(CustomField) ? field.id.to_s : field options = [["", ""], [l(:label_readonly), "readonly"]] - options << [l(:label_required), "required"] unless field_required?(field) + options << [l(:label_required), "required"] unless field_required?(field, trackers) html_options = {} if perm = permissions[status.id][name] diff --git a/app/models/issue.rb b/app/models/issue.rb index 8c3146137..5b1e397d7 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -64,6 +64,7 @@ class Issue < ActiveRecord::Base validates_presence_of :priority, :if => Proc.new {|issue| issue.new_record? || issue.priority_id_changed?} validates_presence_of :status, :if => Proc.new {|issue| issue.new_record? || issue.status_id_changed?} validates_presence_of :author, :if => Proc.new {|issue| issue.new_record? || issue.author_id_changed?} + validates_presence_of :parent_issue_id, :if => Proc.new {|issue| issue.tracker && issue.tracker.subtask_only? && issue.tracker_id_changed?} validates_length_of :subject, :maximum => 255 validates_inclusion_of :done_ratio, :in => 0..100 @@ -623,7 +624,7 @@ class Issue < ActiveRecord::Base # Returns the names of attributes that are read-only for user or the current user # For users with multiple roles, the read-only fields are the intersection of # read-only fields of each role - # The result is an array of strings where sustom fields are represented with their ids + # The result is an array of strings where custom fields are represented with their ids # # Examples: # issue.read_only_attribute_names # => ['due_date', '2'] @@ -635,7 +636,7 @@ class Issue < ActiveRecord::Base # Returns the names of required attributes for user or the current user # For users with multiple roles, the required fields are the intersection of # required fields of each role - # The result is an array of strings where sustom fields are represented with their ids + # The result is an array of strings where custom fields are represented with their ids # # Examples: # issue.required_attribute_names # => ['due_date', '2'] diff --git a/app/models/tracker.rb b/app/models/tracker.rb index b56108ae4..4ad5b7180 100644 --- a/app/models/tracker.rb +++ b/app/models/tracker.rb @@ -70,6 +70,7 @@ class Tracker < ActiveRecord::Base 'name', 'default_status_id', 'is_in_roadmap', + 'subtask_only', 'core_fields', 'position', 'custom_field_ids', diff --git a/app/views/issues/_attributes.html.erb b/app/views/issues/_attributes.html.erb index ee4ae109c..b4b1d8f79 100644 --- a/app/views/issues/_attributes.html.erb +++ b/app/views/issues/_attributes.html.erb @@ -61,8 +61,8 @@
<%= f.text_field :parent_issue_id, :size => 10, - :required => @issue.required_attribute?('parent_issue_id') %>
-<%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript(auto_complete_issues_path(:project_id => @issue.project, :scope => Setting.cross_project_subtasks, :status => @issue.closed? ? 'c' : 'o', :issue_id => @issue.id))}')" %> + :required => @issue.required_attribute?('parent_issue_id') || @issue.tracker.subtask_only? %> +<%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path(:project_id => @issue.project, :scope => Setting.cross_project_subtasks, :status => @issue.closed? ? 'c' : 'o', :issue_id => @issue.id)}')" %> <% end %> <% if @issue.safe_attribute? 'start_date' %> diff --git a/app/views/trackers/_form.html.erb b/app/views/trackers/_form.html.erb index 27e086e56..cbae5cb37 100644 --- a/app/views/trackers/_form.html.erb +++ b/app/views/trackers/_form.html.erb @@ -10,6 +10,7 @@ :include_blank => @tracker.default_status.nil?, :required => true %> +<%= f.check_box :subtask_only %>
<%= f.check_box :is_in_roadmap %>
<%= f.text_area :description, :rows => 4 %>
diff --git a/app/views/trackers/index.api.rsb b/app/views/trackers/index.api.rsb
index 59affd7ae..84bf6160b 100644
--- a/app/views/trackers/index.api.rsb
+++ b/app/views/trackers/index.api.rsb
@@ -4,6 +4,7 @@ api.array :trackers do
api.id tracker.id
api.name tracker.name
api.default_status(:id => tracker.default_status.id, :name => tracker.default_status.name) unless tracker.default_status.nil?
+ api.subtask_only tracker.subtask_only?
api.description tracker.description
end
end
diff --git a/app/views/trackers/index.html.erb b/app/views/trackers/index.html.erb
index 20d09754d..3aea6988f 100644
--- a/app/views/trackers/index.html.erb
+++ b/app/views/trackers/index.html.erb
@@ -9,6 +9,7 @@
<%=l(:label_tracker)%>
<%=l(:field_default_status)%>
+ <%=l(:field_subtask_only)%>
<%=l(:field_description)%>
@@ -18,6 +19,7 @@
<%= link_to tracker.name, edit_tracker_path(tracker) %>
<%= tracker.default_status.name %>
+ <%= checked_image tracker.subtask_only? %>
<%= tracker.description %>
<% unless tracker.workflow_rules.exists? %>
diff --git a/app/views/workflows/permissions.html.erb b/app/views/workflows/permissions.html.erb
index 28a671052..16d63f2ce 100644
--- a/app/views/workflows/permissions.html.erb
+++ b/app/views/workflows/permissions.html.erb
@@ -61,7 +61,7 @@
<% @fields.each do |field, name| %>
- <%= name %> <%= content_tag('span', '*', :class => 'required') if field_required?(field) %>
+ <%= name %> <%= content_tag('span', '*', :class => 'required') if field_required?(field, @trackers) %>
<% for status in @statuses -%>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index b5245a7df..24b759d3b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -403,6 +403,7 @@ en:
field_history_default_tab: Issue's history default tab
field_unique_id: Unique ID
field_toolbar_language_options: Code highlighting toolbar languages
+ field_subtask_only: Subtask only
setting_app_title: Application title
setting_welcome_text: Welcome text
diff --git a/test/fixtures/projects_trackers.yml b/test/fixtures/projects_trackers.yml
index 54443fa0b..f793768c3 100644
--- a/test/fixtures/projects_trackers.yml
+++ b/test/fixtures/projects_trackers.yml
@@ -1,49 +1,52 @@
----
-projects_trackers_001:
+---
+projects_trackers_001:
project_id: 4
tracker_id: 3
-projects_trackers_002:
+projects_trackers_002:
project_id: 1
tracker_id: 1
-projects_trackers_003:
+projects_trackers_003:
project_id: 5
tracker_id: 1
-projects_trackers_004:
+projects_trackers_004:
project_id: 1
tracker_id: 2
-projects_trackers_005:
+projects_trackers_005:
project_id: 5
tracker_id: 2
-projects_trackers_006:
+projects_trackers_006:
project_id: 5
tracker_id: 3
-projects_trackers_007:
+projects_trackers_007:
project_id: 2
tracker_id: 1
-projects_trackers_008:
+projects_trackers_008:
project_id: 2
tracker_id: 2
-projects_trackers_009:
+projects_trackers_009:
project_id: 2
tracker_id: 3
-projects_trackers_010:
+projects_trackers_010:
project_id: 3
tracker_id: 2
-projects_trackers_011:
+projects_trackers_011:
project_id: 3
tracker_id: 3
-projects_trackers_012:
+projects_trackers_012:
project_id: 4
tracker_id: 1
-projects_trackers_013:
+projects_trackers_013:
project_id: 4
tracker_id: 2
-projects_trackers_014:
+projects_trackers_014:
project_id: 1
tracker_id: 3
-projects_trackers_015:
+projects_trackers_015:
project_id: 6
tracker_id: 1
projects_trackers_016:
project_id: 3
tracker_id: 1
+projects_trackers_017:
+ project_id: 1
+ tracker_id: 4
diff --git a/test/fixtures/trackers.yml b/test/fixtures/trackers.yml
index 41602a78f..c57c144af 100644
--- a/test/fixtures/trackers.yml
+++ b/test/fixtures/trackers.yml
@@ -1,21 +1,31 @@
----
-trackers_001:
+---
+trackers_001:
name: Bug
id: 1
is_in_chlog: true
default_status_id: 1
position: 1
description: Description for Bug tracker
-trackers_002:
+ subtask_only: false
+trackers_002:
name: Feature request
id: 2
is_in_chlog: true
default_status_id: 1
position: 2
description: Description for Feature request tracker
-trackers_003:
+ subtask_only: false
+trackers_003:
name: Support request
id: 3
is_in_chlog: false
default_status_id: 1
position: 3
+ subtask_only: false
+trackers_004:
+ name: Subtask
+ id: 4
+ is_in_chlog: false
+ default_status_id: 1
+ position: 4
+ subtask_only: true
diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb
index edf973e6a..5fa9e5115 100644
--- a/test/functional/issues_controller_test.rb
+++ b/test/functional/issues_controller_test.rb
@@ -3207,7 +3207,7 @@ class IssuesControllerTest < Redmine::ControllerTest
get(:new, :params => {:project_id => 1})
assert_response :success
assert_select 'select[name=?]', 'issue[tracker_id]' do
- assert_select 'option', 3
+ assert_select 'option', 4
assert_select 'option[value="1"][selected=selected]'
end
end
@@ -3228,7 +3228,7 @@ class IssuesControllerTest < Redmine::ControllerTest
)
assert_response :success
assert_select 'select[name=?]', 'issue[tracker_id]' do
- assert_select 'option', 2
+ assert_select 'option', 3
assert_select 'option[value="2"][selected=selected]'
assert_select 'option[value="1"]', 0
end
diff --git a/test/functional/trackers_controller_test.rb b/test/functional/trackers_controller_test.rb
index bc770e5d7..659c093f6 100644
--- a/test/functional/trackers_controller_test.rb
+++ b/test/functional/trackers_controller_test.rb
@@ -31,6 +31,9 @@ class TrackersControllerTest < Redmine::ControllerTest
get :index
assert_response :success
assert_select 'table.trackers'
+
+ # assert subtask column
+ assert_select 'td.subtask .icon-checked', 1
end
def test_index_by_anonymous_should_redirect_to_login_form
@@ -49,6 +52,7 @@ class TrackersControllerTest < Redmine::ControllerTest
get :new
assert_response :success
assert_select 'input[name=?]', 'tracker[name]'
+ assert_select 'input[name=?]', 'tracker[subtask_only]'
assert_select 'select[name=?]', 'tracker[default_status_id]' do
assert_select 'option[value=?][selected=selected]', IssueStatus.sorted.first.id.to_s
end
@@ -72,6 +76,7 @@ class TrackersControllerTest < Redmine::ControllerTest
assert_equal Tracker::CORE_FIELDS, tracker.core_fields
assert_equal [1, 6], tracker.custom_field_ids.sort
assert_equal 0, tracker.workflow_rules.count
+ assert !tracker.subtask_only
end
def test_create_with_disabled_core_fields
diff --git a/test/functional/workflows_controller_test.rb b/test/functional/workflows_controller_test.rb
index cc535c42d..f4ac24a12 100644
--- a/test/functional/workflows_controller_test.rb
+++ b/test/functional/workflows_controller_test.rb
@@ -246,6 +246,31 @@ class WorkflowsControllerTest < Redmine::ControllerTest
end
end
+ def test_get_permissions_with_role_and_subtask_tracker_should_mark_parent_task_field_required
+ get :permissions, :params => {:role_id => 1, :tracker_id => 4}
+ assert_response :success
+
+ assert_select 'td.name', :text => 'Parent task *'
+ end
+
+ def test_get_permissions_with_role_and_multiple_subtask_trackers_should_mark_parent_task_field_required
+ subtask = Tracker.find(1)
+ subtask.subtask_only = true
+ subtask.save!
+
+ get :permissions, :params => {:role_id => 1, :tracker_id => [1, 4]}
+ assert_response :success
+
+ assert_select 'td.name', :text => 'Parent task *'
+ end
+
+ def test_get_permissions_with_role_and_mixed_trackers_should_not_mark_parent_task_field_required
+ get :permissions, :params => {:role_id => 1, :tracker_id => [1, 4]}
+ assert_response :success
+
+ assert_select 'td.name', :text => 'Parent task'
+ end
+
def test_get_permissions_with_required_custom_field_should_not_show_required_option
cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :tracker_ids => [1], :is_required => true)
diff --git a/test/integration/api_test/trackers_test.rb b/test/integration/api_test/trackers_test.rb
index 5f220024d..a1bde73f1 100644
--- a/test/integration/api_test/trackers_test.rb
+++ b/test/integration/api_test/trackers_test.rb
@@ -30,7 +30,12 @@ class Redmine::ApiTest::TrackersTest < Redmine::ApiTest::Base
assert_select 'trackers[type=array] tracker id', :text => '2' do
assert_select '~ name', :text => 'Feature request'
+ assert_select '~ subtask_only', :text => 'false'
assert_select '~ description', :text => 'Description for Feature request tracker'
end
+
+ assert_select 'trackers[type=array] tracker id', :text => '4' do
+ assert_select '~ subtask_only', :text => 'true'
+ end
end
end
diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb
index 6c3ae4ccc..ed90162d8 100644
--- a/test/unit/issue_test.rb
+++ b/test/unit/issue_test.rb
@@ -214,6 +214,23 @@ class IssueTest < ActiveSupport::TestCase
assert_include 'Parent task is invalid', issue.errors.full_messages
end
+ def test_create_with_subtask_tracker
+ issue = Issue.new(:project_id => 1, :tracker_id => 4,
+ :author_id => 1, :subject => 'Group assignment',
+ :parent_issue_id => 1)
+
+ assert issue.save
+ assert_equal 1, issue.parent_issue_id
+ end
+
+ def test_create_with_subtask_tracker_should_require_parent_task
+ issue = Issue.new(:project_id => 1, :tracker_id => 4,
+ :author_id => 1, :subject => 'Group assignment')
+
+ assert !issue.save
+ assert_include 'Parent task cannot be blank', issue.errors.full_messages
+ end
+
def assert_visibility_match(user, issues)
assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
end
@@ -1706,7 +1723,7 @@ class IssueTest < ActiveSupport::TestCase
role.save!
User.add_to_project(user, Project.find(1), role)
- assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
+ assert_equal [1, 2, 3, 4], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
end
def test_allowed_target_trackers_with_one_role_allowed_on_some_trackers
@@ -1747,7 +1764,7 @@ class IssueTest < ActiveSupport::TestCase
role2.save!
User.add_to_project(user, Project.find(1), [role1, role2])
- assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
+ assert_equal [1, 2, 3, 4], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
end
def test_allowed_target_trackers_should_not_consider_roles_without_add_issues_permission
diff --git a/test/unit/lib/redmine/acts/positioned_without_scope_test.rb b/test/unit/lib/redmine/acts/positioned_without_scope_test.rb
index 79a95699a..73a5bba1e 100644
--- a/test/unit/lib/redmine/acts/positioned_without_scope_test.rb
+++ b/test/unit/lib/redmine/acts/positioned_without_scope_test.rb
@@ -26,7 +26,7 @@ class Redmine::Acts::PositionedWithoutScopeTest < ActiveSupport::TestCase
t = Tracker.generate
t.save!
- assert_equal 4, t.reload.position
+ assert_equal 5, t.reload.position
end
def test_create_should_insert_at_given_position
@@ -35,7 +35,7 @@ class Redmine::Acts::PositionedWithoutScopeTest < ActiveSupport::TestCase
t.save!
assert_equal 2, t.reload.position
- assert_equal [1, 3, 4, 2], Tracker.order(:id).pluck(:position)
+ assert_equal [1, 3, 4, 5, 2], Tracker.order(:id).pluck(:position)
end
def test_destroy_should_remove_position
@@ -43,15 +43,15 @@ class Redmine::Acts::PositionedWithoutScopeTest < ActiveSupport::TestCase
Tracker.generate!
t.destroy
- assert_equal [1, 2, 3, 4], Tracker.order(:id).pluck(:position)
+ assert_equal [1, 2, 3, 4, 5], Tracker.order(:id).pluck(:position)
end
def test_update_should_update_positions
t = Tracker.generate!
- assert_equal 4, t.position
+ assert_equal 5, t.position
t.position = 2
t.save!
- assert_equal [1, 3, 4, 2], Tracker.order(:id).pluck(:position)
+ assert_equal [1, 3, 4, 5, 2], Tracker.order(:id).pluck(:position)
end
end
diff --git a/test/unit/tracker_test.rb b/test/unit/tracker_test.rb
index 89a711fdd..1e2827fbd 100644
--- a/test/unit/tracker_test.rb
+++ b/test/unit/tracker_test.rb
@@ -140,4 +140,12 @@ class TrackerTest < ActiveSupport::TestCase
assert tracker.respond_to?(:description)
assert_equal tracker.description, "Description for Bug tracker"
end
+
+ def test_set_tracker_subtask_only
+ tracker = Tracker.find(1)
+ tracker.subtask_only = true
+
+ assert tracker.save!
+ assert tracker.subtask_only?
+ end
end