Feature #5358 » redmine-5358.diff
| app/models/issue.rb | ||
|---|---|---|
| 143 | 143 |
unless new_project.shared_versions.include?(issue.fixed_version) |
| 144 | 144 |
issue.fixed_version = nil |
| 145 | 145 |
end |
| 146 |
# Keep the category if it's still valid in the new_project |
|
| 147 |
unless new_project.shared_categories.include?(issue.category) |
|
| 148 |
issue.category = nil |
|
| 149 |
end |
|
| 146 | 150 |
issue.project = new_project |
| 147 | 151 |
if issue.parent && issue.parent.project_id != issue.project_id |
| 148 | 152 |
issue.parent_issue_id = nil |
| ... | ... | |
| 414 | 418 |
def assignable_versions |
| 415 | 419 |
@assignable_versions ||= (project.shared_versions.open + [Version.find_by_id(fixed_version_id_was)]).compact.uniq.sort |
| 416 | 420 |
end |
| 421 | ||
| 422 |
# Categories that the issue can be assigned to |
|
| 423 |
def assignable_categories |
|
| 424 |
@assignable_categories ||= (project.shared_categories.open + [IssueCategory.find_by_id(issue_category_id_was)]).compact.uniq.sort |
|
| 425 |
end |
|
| 417 | 426 |
|
| 418 | 427 |
# Returns true if this issue is blocked by another issue that is still open |
| 419 | 428 |
def blocked? |
| ... | ... | |
| 573 | 582 |
# Update issues assigned to the version |
| 574 | 583 |
update_versions(["#{Issue.table_name}.fixed_version_id = ?", version.id])
|
| 575 | 584 |
end |
| 585 | ||
| 586 |
# Unassigns issues from +category+ if it's no longer shared with issue's project |
|
| 587 |
def self.update_categories_from_sharing_change(version) |
|
| 588 |
# Update issues assigned to the version |
|
| 589 |
update_categories(["#{Issue.table_name}.category_id = ?", category.id])
|
|
| 590 |
end |
|
| 576 | 591 |
|
| 577 | 592 |
# Unassigns issues from versions that are no longer shared |
| 578 | 593 |
# after +project+ was moved |
| ... | ... | |
| 776 | 791 |
end |
| 777 | 792 |
end |
| 778 | 793 |
end |
| 794 | ||
| 795 |
# Update issues so their categories are not pointing to a |
|
| 796 |
# fixed_version that is not shared with the issue's project |
|
| 797 |
def self.update_categories(conditions=nil) |
|
| 798 |
# Only need to update issues with a fixed_version from |
|
| 799 |
# a different project and that is not systemwide shared |
|
| 800 |
Issue.all(:conditions => merge_conditions("#{Issue.table_name}.category_id IS NOT NULL" +
|
|
| 801 |
" AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
|
|
| 802 |
" AND #{IssueCategory.table_name}.sharing <> 'system'",
|
|
| 803 |
conditions), |
|
| 804 |
:include => [:project, :category] |
|
| 805 |
).each do |issue| |
|
| 806 |
next if issue.project.nil? || issue.category.nil? |
|
| 807 |
unless issue.project.shared_categories.include?(issue.category) |
|
| 808 |
issue.init_journal(User.current) |
|
| 809 |
issue.category = nil |
|
| 810 |
issue.save |
|
| 811 |
end |
|
| 812 |
end |
|
| 813 |
end |
|
| 779 | 814 |
|
| 780 | 815 |
# Callback on attachment deletion |
| 781 | 816 |
def attachment_removed(obj) |
| app/models/issue_category.rb | ||
|---|---|---|
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 17 | 17 | |
| 18 | 18 |
class IssueCategory < ActiveRecord::Base |
| 19 |
after_update :update_issues_from_sharing_change |
|
| 19 | 20 |
belongs_to :project |
| 20 | 21 |
belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id' |
| 21 | 22 |
has_many :issues, :foreign_key => 'category_id', :dependent => :nullify |
| 23 | ||
| 24 |
SHARINGS = %w(none descendants hierarchy tree system) |
|
| 22 | 25 |
|
| 23 | 26 |
validates_presence_of :name |
| 24 | 27 |
validates_uniqueness_of :name, :scope => [:project_id] |
| 25 | 28 |
validates_length_of :name, :maximum => 30 |
| 29 |
validates_inclusion_of :sharing, :in => VERSION_SHARINGS |
|
| 26 | 30 |
|
| 27 | 31 |
alias :destroy_without_reassign :destroy |
| 28 | 32 |
|
| ... | ... | |
| 40 | 44 |
end |
| 41 | 45 |
|
| 42 | 46 |
def to_s; name end |
| 47 | ||
| 48 |
# Returns the sharings that +user+ can set the category to |
|
| 49 |
def allowed_sharings(user = User.current) |
|
| 50 |
SHARINGS.select do |s| |
|
| 51 |
if sharing == s |
|
| 52 |
true |
|
| 53 |
else |
|
| 54 |
case s |
|
| 55 |
when 'system' |
|
| 56 |
# Only admin users can set a systemwide sharing |
|
| 57 |
user.admin? |
|
| 58 |
when 'hierarchy', 'tree' |
|
| 59 |
# Only users allowed to manage versions of the root project can |
|
| 60 |
# set sharing to hierarchy or tree |
|
| 61 |
project.nil? || user.allowed_to?(:manage_versions, project.root) |
|
| 62 |
else |
|
| 63 |
true |
|
| 64 |
end |
|
| 65 |
end |
|
| 66 |
end |
|
| 67 |
end |
|
| 68 | ||
| 69 |
# Update the issue's fixed versions. Used if a version's sharing changes. |
|
| 70 |
def update_issues_from_sharing_change |
|
| 71 |
if sharing_changed? |
|
| 72 |
if SHARINGS.index(sharing_was).nil? || |
|
| 73 |
SHARINGS.index(sharing).nil? || |
|
| 74 |
SHARINGS.index(sharing_was) > SHARINGS.index(sharing) |
|
| 75 |
Issue.update_categories_from_sharing_change self |
|
| 76 |
end |
|
| 77 |
end |
|
| 78 |
end |
|
| 43 | 79 |
end |
| app/models/project.rb | ||
|---|---|---|
| 384 | 384 |
"))") |
| 385 | 385 |
end |
| 386 | 386 | |
| 387 |
# Returns a scope of the Versions used by the project |
|
| 388 |
def shared_categories |
|
| 389 |
@shared_categories ||= |
|
| 390 |
IssueCategory.scoped(:include => :project, |
|
| 391 |
:conditions => "#{Project.table_name}.id = #{id}" +
|
|
| 392 |
" OR (#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND (" +
|
|
| 393 |
" #{IssueCategory.table_name}.sharing = 'system'" +
|
|
| 394 |
" OR (#{Project.table_name}.lft >= #{root.lft} AND #{Project.table_name}.rgt <= #{root.rgt} AND #{Version.table_name}.sharing = 'tree')" +
|
|
| 395 |
" OR (#{Project.table_name}.lft < #{lft} AND #{Project.table_name}.rgt > #{rgt} AND #{IssueCategory.table_name}.sharing IN ('hierarchy', 'descendants'))" +
|
|
| 396 |
" OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt} AND #{IssueCategory.table_name}.sharing = 'hierarchy')" +
|
|
| 397 |
"))") |
|
| 398 |
end |
|
| 399 | ||
| 387 | 400 |
# Returns a hash of project users grouped by role |
| 388 | 401 |
def users_by_role |
| 389 | 402 |
members.find(:all, :include => [:user, :roles]).inject({}) do |h, m|
|
| app/views/issue_categories/_form.rhtml | ||
|---|---|---|
| 3 | 3 |
<div class="box"> |
| 4 | 4 |
<p><%= f.text_field :name, :size => 30, :required => true %></p> |
| 5 | 5 |
<p><%= f.select :assigned_to_id, @project.users.sort.collect{|u| [u.name, u.id]}, :include_blank => true %></p>
|
| 6 |
<p><%= f.select :sharing, @category.allowed_sharings.collect{|c| [format_version_sharing(c), c]} %></p>
|
|
| 6 | 7 |
</div> |
| app/views/issues/_attributes.rhtml | ||
|---|---|---|
| 9 | 9 | |
| 10 | 10 |
<p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), {:required => true}, :disabled => !@issue.leaf? %></p>
|
| 11 | 11 |
<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
|
| 12 |
<% unless @project.issue_categories.empty? %>
|
|
| 13 |
<p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
|
|
| 12 |
<% unless @issue.assignable_categories.empty? %>
|
|
| 13 |
<p><%= f.select :category_id, (@issue.assignable_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
|
|
| 14 | 14 |
<%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
|
| 15 | 15 |
l(:label_issue_category_new), |
| 16 | 16 |
'category[name]', |
| db/migrate/20110216000000_add_issue_category_sharing.rb | ||
|---|---|---|
| 1 |
class AddVersionsSharing < ActiveRecord::Migration |
|
| 2 |
def self.up |
|
| 3 |
add_column :issue_categories, :sharing, :string, :default => 'none', :null => false |
|
| 4 |
add_index :issue_categories, :sharing |
|
| 5 |
end |
|
| 6 | ||
| 7 |
def self.down |
|
| 8 |
remove_column :issue_categories, :sharing |
|
| 9 |
end |
|
| 10 |
end |
|