Adds subtasking (#443) including:

* priority, start/due dates, progress, estimate, spent time roll-up to parent issues
* descendant issues tree displayed on the issue view with context menu support
* issue tree display on the gantt chart
* issue tree copy on project copy
* unlimited nesting

Defining subtasks requires the new permission 'Manage subtasks'.
Subtasks can not belong to a different project than the parent task.

Implementation is based on scoped nested sets for fast reads and updates.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3573 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang
2010-03-13 14:56:49 +00:00
parent e109c9b6b6
commit 8e3d1b694a
28 changed files with 855 additions and 93 deletions
+2 -1
View File
@@ -47,13 +47,14 @@ Redmine::AccessControl.map do |map|
map.permission :manage_categories, {:projects => :settings, :issue_categories => [:new, :edit, :destroy]}, :require => :member
# Issues
map.permission :view_issues, {:projects => :roadmap,
:issues => [:index, :changes, :show, :context_menu],
:issues => [:index, :changes, :show, :context_menu, :auto_complete],
:versions => [:show, :status_by],
:queries => :index,
:reports => [:issue_report, :issue_report_details]}
map.permission :add_issues, {:issues => [:new, :update_form]}
map.permission :edit_issues, {:issues => [:edit, :update, :reply, :bulk_edit, :update_form]}
map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
map.permission :manage_subtasks, {}
map.permission :add_issue_notes, {:issues => [:edit, :update, :reply]}
map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin
+1
View File
@@ -53,6 +53,7 @@ module Redmine
:add_issues,
:edit_issues,
:manage_issue_relations,
:manage_subtasks,
:add_issue_notes,
:save_queries,
:view_gantt,
+52 -1
View File
@@ -52,8 +52,29 @@ module Redmine
@date_to = (@date_from >> @months) - 1
end
def events=(e)
@events = e.sort {|x,y| x.start_date <=> y.start_date }
@events = e
# Adds all ancestors
root_ids = e.select {|i| i.is_a?(Issue) && i.parent_id? }.collect(&:root_id).uniq
if root_ids.any?
# Retrieves all nodes
parents = Issue.find_all_by_root_id(root_ids, :conditions => ["rgt - lft > 1"])
# Only add ancestors
@events += parents.select {|p| @events.detect {|i| i.is_a?(Issue) && p.is_ancestor_of?(i)}}
end
@events.uniq!
# Sort issues by hierarchy and start dates
@events.sort! {|x,y|
if x.is_a?(Issue) && y.is_a?(Issue)
gantt_issue_compare(x, y, @events)
else
gantt_start_compare(x, y)
end
}
# Removes issues that have no start or end date
@events.reject! {|i| i.is_a?(Issue) && (i.start_date.nil? || i.due_before.nil?) }
@events
end
def params
@@ -218,6 +239,36 @@ module Redmine
imgl.format = format
imgl.to_blob
end if Object.const_defined?(:Magick)
private
def gantt_issue_compare(x, y, issues)
if x.parent_id == y.parent_id
gantt_start_compare(x, y)
elsif x.is_ancestor_of?(y)
-1
elsif y.is_ancestor_of?(x)
1
else
ax = issues.select {|i| i.is_a?(Issue) && i.is_ancestor_of?(x) && !i.is_ancestor_of?(y) }.sort_by(&:lft).first
ay = issues.select {|i| i.is_a?(Issue) && i.is_ancestor_of?(y) && !i.is_ancestor_of?(x) }.sort_by(&:lft).first
if ax.nil? && ay.nil?
gantt_start_compare(x, y)
else
gantt_issue_compare(ax || x, ay || y, issues)
end
end
end
def gantt_start_compare(x, y)
if x.start_date.nil?
-1
elsif y.start_date.nil?
1
else
x.start_date <=> y.start_date
end
end
end
end
end