# HG changeset patch # User Leo Shklovskii # Date 1329958127 28800 # Node ID 2cd0fa8c20b70c0da646e0cd3bf6b1882cac328e # Parent b189870d01ccd6ab145885b7e3a6d4d5d014cffc category sharing patch diff -r b189870d01cc -r 2cd0fa8c20b7 app/controllers/issue_categories_controller.rb --- a/app/controllers/issue_categories_controller.rb Wed Feb 22 14:09:19 2012 -0800 +++ b/app/controllers/issue_categories_controller.rb Wed Feb 22 16:48:47 2012 -0800 @@ -43,6 +43,9 @@ end verify :method => :post, :only => :create + + helper :projects + def create @category = @project.issue_categories.build(params[:issue_category]) if @category.save diff -r b189870d01cc -r 2cd0fa8c20b7 app/controllers/reports_controller.rb --- a/app/controllers/reports_controller.rb Wed Feb 22 14:09:19 2012 -0800 +++ b/app/controllers/reports_controller.rb Wed Feb 22 16:48:47 2012 -0800 @@ -23,7 +23,7 @@ @trackers = @project.trackers @versions = @project.shared_versions.sort @priorities = IssuePriority.all - @categories = @project.issue_categories + @categories = @project.share_categories.sort @assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort @authors = @project.users.sort @subprojects = @project.descendants.visible @@ -58,7 +58,7 @@ @report_title = l(:field_priority) when "category" @field = "category_id" - @rows = @project.issue_categories + @rows = @project.shared_categories.sort @data = Issue.by_category(@project) @report_title = l(:field_category) when "assigned_to" diff -r b189870d01cc -r 2cd0fa8c20b7 app/helpers/projects_helper.rb --- a/app/helpers/projects_helper.rb Wed Feb 22 14:09:19 2012 -0800 +++ b/app/helpers/projects_helper.rb Wed Feb 22 16:48:47 2012 -0800 @@ -107,4 +107,17 @@ sharing = 'none' unless Version::VERSION_SHARINGS.include?(sharing) l("label_version_sharing_#{sharing}") end + + def format_category_sharing(category, project) + sharing = IssueCategory::SHARINGS.include?(category.sharing) ? category.sharing : 'none' + project_name = category.project != project ? ' (' + category.project.name + ')' : '' + # sharing = category.sharing + # sharing = 'none' unless IssueCategory::SHARINGS.include?(category.sharing) + # if category.project != project + # project_name = ' (' + category.project.name + ')' + # else + # project_name = '' + # end + l("label_version_sharing_#{sharing}") + project_name + end end diff -r b189870d01cc -r 2cd0fa8c20b7 app/models/issue.rb --- a/app/models/issue.rb Wed Feb 22 14:09:19 2012 -0800 +++ b/app/models/issue.rb Wed Feb 22 16:48:47 2012 -0800 @@ -168,6 +168,10 @@ unless new_project.shared_versions.include?(issue.fixed_version) issue.fixed_version = nil end + # Keep the fixed_category if it's still valid in the new_project + unless new_project.shared_categories.include?(issue.category) + issue.category = nil + end issue.project = new_project if issue.parent && issue.parent.project_id != issue.project_id issue.parent_issue_id = nil @@ -359,6 +363,12 @@ end end + if category + if !assignable_categories.include?(category) + errors.add :category_id, :inclusion + end + end + # Checks that the issue can not be added/moved to a disabled tracker if project && (tracker_id_changed? || project_id_changed?) unless project.trackers.include?(tracker) @@ -461,6 +471,11 @@ @assignable_versions ||= (project.shared_versions.open + [Version.find_by_id(fixed_version_id_was)]).compact.uniq.sort end + # Categories that the issue can be assigned to + def assignable_categories + @assignable_categories ||= (project.shared_categories + [IssueCategory.find_by_id(category_id_was)]).compact.uniq.sort + end + # Returns true if this issue is blocked by another issue that is still open def blocked? !relations_to.detect {|ir| ir.relation_type == 'blocks' && !ir.issue_from.closed?}.nil? @@ -647,6 +662,12 @@ update_versions(["#{Issue.table_name}.fixed_version_id = ?", version.id]) end + # Unassigns issues from +category+ if it's no longer shared with issue's project + def self.update_categories_from_sharing_change(category) + # Update issues assigned to the category + update_categories(["#{Issue.table_name}.category_id = ?", category.id]) + end + # Unassigns issues from versions that are no longer shared # after +project+ was moved def self.update_versions_from_hierarchy_change(project) @@ -655,6 +676,14 @@ Issue.update_versions(["#{Version.table_name}.project_id IN (?) OR #{Issue.table_name}.project_id IN (?)", moved_project_ids, moved_project_ids]) end + # Unassigns issues from categories that are no longer shared + # after +project+ was moved + def self.update_categories_from_hierarchy_change(project) + moved_project_ids = project.self_and_descendants.reload.collect(&:id) + # Update issues of the moved projects and issues assigned to a category of a moved project + Issue.update_categories(["#{Version.table_name}.project_id IN (?) OR #{Issue.table_name}.project_id IN (?)", moved_project_ids, moved_project_ids]) + end + def parent_issue_id=(arg) parent_issue_id = arg.blank? ? nil : arg.to_i if parent_issue_id && @parent_issue = Issue.find_by_id(parent_issue_id) @@ -852,6 +881,26 @@ end end + # Update issues so their categories are not pointing to a + # fixed_version that is not shared with the issue's project + def self.update_categories(conditions=nil) + # Only need to update issues with a fixed_version from + # a different project and that is not systemwide shared + Issue.all(:conditions => merge_conditions("#{Issue.table_name}.category_id IS NOT NULL" + + " AND #{Issue.table_name}.project_id <> #{IssueCategory.table_name}.project_id" + + " AND #{IssueCategory.table_name}.sharing <> 'system'", + conditions), + :include => [:project, :category] + ).each do |issue| + next if issue.project.nil? || issue.category.nil? + unless issue.project.shared_categories.include?(issue.category) + issue.init_journal(User.current) + issue.category = nil + issue.save + end + end + end + # Callback on attachment deletion def attachment_added(obj) if @current_journal && !obj.new_record? diff -r b189870d01cc -r 2cd0fa8c20b7 app/models/issue_category.rb --- a/app/models/issue_category.rb Wed Feb 22 14:09:19 2012 -0800 +++ b/app/models/issue_category.rb Wed Feb 22 16:48:47 2012 -0800 @@ -16,14 +16,18 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class IssueCategory < ActiveRecord::Base + after_update :update_issues_from_sharing_change belongs_to :project belongs_to :assigned_to, :class_name => 'Principal', :foreign_key => 'assigned_to_id' has_many :issues, :foreign_key => 'category_id', :dependent => :nullify + SHARINGS = %w(none descendants hierarchy tree system) + validates_presence_of :name validates_uniqueness_of :name, :scope => [:project_id] validates_length_of :name, :maximum => 30 - + validates_inclusion_of :sharing, :in => SHARINGS + attr_protected :project_id named_scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}} @@ -44,4 +48,35 @@ end def to_s; name end + # Returns the sharings that +user+ can set the category to + def allowed_sharings(user = User.current) + SHARINGS.select do |s| + if sharing == s + true + else + case s + when 'system' + # Only admin users can set a systemwide sharing + user.admin? + when 'hierarchy', 'tree' + # Only users allowed to manage categories of the root project can + # set sharing to hierarchy or tree + project.nil? || user.allowed_to?(:manage_categories, project.root) + else + true + end + end + end + end + + # Update the issue's fixed categories. Used if a category's sharing changes. + def update_issues_from_sharing_change + if sharing_changed? + if SHARINGS.index(sharing_was).nil? || + SHARINGS.index(sharing).nil? || + SHARINGS.index(sharing_was) > SHARINGS.index(sharing) + Issue.update_categories_from_sharing_change self + end + end + end end diff -r b189870d01cc -r 2cd0fa8c20b7 app/models/mail_handler.rb --- a/app/models/mail_handler.rb Wed Feb 22 14:09:19 2012 -0800 +++ b/app/models/mail_handler.rb Wed Feb 22 16:48:47 2012 -0800 @@ -267,7 +267,7 @@ 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id), 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id), 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id), - 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id), + 'category_id' => (k = get_keyword(:category)) && issue.project.shared_categories.named(k).first.try(:id), 'assigned_to_id' => assigned_to.try(:id), 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && issue.project.shared_versions.named(k).first.try(:id), 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), diff -r b189870d01cc -r 2cd0fa8c20b7 app/models/project.rb --- a/app/models/project.rb Wed Feb 22 14:09:19 2012 -0800 +++ b/app/models/project.rb Wed Feb 22 16:48:47 2012 -0800 @@ -354,6 +354,7 @@ move_to_child_of(p) end Issue.update_versions_from_hierarchy_change(self) + Issue.update_categories_from_hierarchy_change(self) true else # Can not move to the given target @@ -402,6 +403,18 @@ "))") end end + # Returns a scope of the categories used by the project + def shared_categories + @shared_categories ||= + IssueCategory.find(:all, :include => :project, + :conditions => "#{Project.table_name}.id = #{id}" + + " OR (#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND (" + + " #{IssueCategory.table_name}.sharing = 'system'" + + " OR (#{Project.table_name}.lft >= #{root.lft} AND #{Project.table_name}.rgt <= #{root.rgt} AND #{IssueCategory.table_name}.sharing = 'tree')" + + " OR (#{Project.table_name}.lft < #{lft} AND #{Project.table_name}.rgt > #{rgt} AND #{IssueCategory.table_name}.sharing IN ('hierarchy', 'descendants'))" + + " OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt} AND #{IssueCategory.table_name}.sharing = 'hierarchy')" + + "))") + end # Returns a hash of project users grouped by role def users_by_role diff -r b189870d01cc -r 2cd0fa8c20b7 app/models/query.rb --- a/app/models/query.rb Wed Feb 22 14:09:19 2012 -0800 +++ b/app/models/query.rb Wed Feb 22 16:48:47 2012 -0800 @@ -270,9 +270,9 @@ if project # project specific filters - categories = project.issue_categories.all + categories = project.shared_categories unless categories.empty? - @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => categories.collect{|s| [s.name, s.id.to_s] } } + @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => categories.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } } end versions = project.shared_versions.all unless versions.empty? diff -r b189870d01cc -r 2cd0fa8c20b7 app/views/context_menus/issues.html.erb --- a/app/views/context_menus/issues.html.erb Wed Feb 22 14:09:19 2012 -0800 +++ b/app/views/context_menus/issues.html.erb Wed Feb 22 16:48:47 2012 -0800 @@ -70,11 +70,11 @@ <% end %> - <% unless @project.nil? || @project.issue_categories.empty? -%> + <% unless @project.nil? || @project.shared_categories.empty? -%>
  • <%= l(:field_category) %>