Mercurial > hg > soundsoftware-site
comparison app/models/project.rb @ 1295:622f24f53b42 redmine-2.3
Update to Redmine SVN revision 11972 on 2.3-stable branch
author | Chris Cannam |
---|---|
date | Fri, 14 Jun 2013 09:02:21 +0100 |
parents | 3e4c3460b6ca |
children | 4f746d8966dd |
comparison
equal
deleted
inserted
replaced
1294:3e4c3460b6ca | 1295:622f24f53b42 |
---|---|
1 # Redmine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006-2012 Jean-Philippe Lang | 2 # Copyright (C) 2006-2013 Jean-Philippe Lang |
3 # | 3 # |
4 # This program is free software; you can redistribute it and/or | 4 # This program is free software; you can redistribute it and/or |
5 # modify it under the terms of the GNU General Public License | 5 # modify it under the terms of the GNU General Public License |
6 # as published by the Free Software Foundation; either version 2 | 6 # as published by the Free Software Foundation; either version 2 |
7 # of the License, or (at your option) any later version. | 7 # of the License, or (at your option) any later version. |
26 # Maximum length for project identifiers | 26 # Maximum length for project identifiers |
27 IDENTIFIER_MAX_LENGTH = 100 | 27 IDENTIFIER_MAX_LENGTH = 100 |
28 | 28 |
29 # Specific overidden Activities | 29 # Specific overidden Activities |
30 has_many :time_entry_activities | 30 has_many :time_entry_activities |
31 has_many :members, :include => [:principal, :roles], :conditions => "#{User.table_name}.type='User' AND #{User.table_name}.status=#{User::STATUS_ACTIVE}" | 31 has_many :members, :include => [:principal, :roles], :conditions => "#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}" |
32 has_many :memberships, :class_name => 'Member' | 32 has_many :memberships, :class_name => 'Member' |
33 has_many :member_principals, :class_name => 'Member', | 33 has_many :member_principals, :class_name => 'Member', |
34 :include => :principal, | 34 :include => :principal, |
35 :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{User::STATUS_ACTIVE})" | 35 :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE})" |
36 has_many :users, :through => :members | 36 has_many :users, :through => :members |
37 has_many :principals, :through => :member_principals, :source => :principal | 37 has_many :principals, :through => :member_principals, :source => :principal |
38 | 38 |
39 has_many :enabled_modules, :dependent => :delete_all | 39 has_many :enabled_modules, :dependent => :delete_all |
40 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position" | 40 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position" |
41 has_many :issues, :dependent => :destroy, :include => [:status, :tracker] | 41 has_many :issues, :dependent => :destroy, :include => [:status, :tracker] |
42 has_many :issue_changes, :through => :issues, :source => :journals | 42 has_many :issue_changes, :through => :issues, :source => :journals |
43 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC" | 43 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC" |
44 has_many :time_entries, :dependent => :delete_all | 44 has_many :time_entries, :dependent => :delete_all |
45 has_many :queries, :dependent => :delete_all | 45 has_many :queries, :class_name => 'IssueQuery', :dependent => :delete_all |
46 has_many :documents, :dependent => :destroy | 46 has_many :documents, :dependent => :destroy |
47 has_many :news, :dependent => :destroy, :include => :author | 47 has_many :news, :dependent => :destroy, :include => :author |
48 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name" | 48 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name" |
49 has_many :boards, :dependent => :destroy, :order => "position ASC" | 49 has_many :boards, :dependent => :destroy, :order => "position ASC" |
50 has_one :repository, :conditions => ["is_default = ?", true] | 50 has_one :repository, :conditions => ["is_default = ?", true] |
75 validates_associated :repository, :wiki | 75 validates_associated :repository, :wiki |
76 validates_length_of :name, :maximum => 255 | 76 validates_length_of :name, :maximum => 255 |
77 validates_length_of :homepage, :maximum => 255 | 77 validates_length_of :homepage, :maximum => 255 |
78 validates_length_of :identifier, :in => 1..IDENTIFIER_MAX_LENGTH | 78 validates_length_of :identifier, :in => 1..IDENTIFIER_MAX_LENGTH |
79 # donwcase letters, digits, dashes but not digits only | 79 # donwcase letters, digits, dashes but not digits only |
80 validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-_]*$/, :if => Proc.new { |p| p.identifier_changed? } | 80 validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :if => Proc.new { |p| p.identifier_changed? } |
81 # reserved words | 81 # reserved words |
82 validates_exclusion_of :identifier, :in => %w( new ) | 82 validates_exclusion_of :identifier, :in => %w( new ) |
83 | 83 |
84 after_save :update_position_under_parent, :if => Proc.new {|project| project.name_changed?} | 84 after_save :update_position_under_parent, :if => Proc.new {|project| project.name_changed?} |
85 after_save :update_inherited_members, :if => Proc.new {|project| project.inherit_members_changed?} | |
85 before_destroy :delete_all_members | 86 before_destroy :delete_all_members |
86 | 87 |
87 scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } } | 88 scope :has_module, lambda {|mod| |
88 scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"} | 89 where("#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s) |
89 scope :status, lambda {|arg| arg.blank? ? {} : {:conditions => {:status => arg.to_i}} } | 90 } |
90 scope :all_public, { :conditions => { :is_public => true } } | 91 scope :active, lambda { where(:status => STATUS_ACTIVE) } |
91 scope :visible, lambda {|*args| {:conditions => Project.visible_condition(args.shift || User.current, *args) }} | 92 scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) } |
93 scope :all_public, lambda { where(:is_public => true) } | |
94 scope :visible, lambda {|*args| where(Project.visible_condition(args.shift || User.current, *args)) } | |
92 scope :allowed_to, lambda {|*args| | 95 scope :allowed_to, lambda {|*args| |
93 user = User.current | 96 user = User.current |
94 permission = nil | 97 permission = nil |
95 if args.first.is_a?(Symbol) | 98 if args.first.is_a?(Symbol) |
96 permission = args.shift | 99 permission = args.shift |
97 else | 100 else |
98 user = args.shift | 101 user = args.shift |
99 permission = args.shift | 102 permission = args.shift |
100 end | 103 end |
101 { :conditions => Project.allowed_to_condition(user, permission, *args) } | 104 where(Project.allowed_to_condition(user, permission, *args)) |
102 } | 105 } |
103 scope :like, lambda {|arg| | 106 scope :like, lambda {|arg| |
104 if arg.blank? | 107 if arg.blank? |
105 {} | 108 where(nil) |
106 else | 109 else |
107 pattern = "%#{arg.to_s.strip.downcase}%" | 110 pattern = "%#{arg.to_s.strip.downcase}%" |
108 {:conditions => ["LOWER(identifier) LIKE :p OR LOWER(name) LIKE :p", {:p => pattern}]} | 111 where("LOWER(identifier) LIKE :p OR LOWER(name) LIKE :p", :p => pattern) |
109 end | 112 end |
110 } | 113 } |
111 | 114 |
112 def initialize(attributes=nil, *args) | 115 def initialize(attributes=nil, *args) |
113 super | 116 super |
121 end | 124 end |
122 if !initialized.key?('enabled_module_names') | 125 if !initialized.key?('enabled_module_names') |
123 self.enabled_module_names = Setting.default_projects_modules | 126 self.enabled_module_names = Setting.default_projects_modules |
124 end | 127 end |
125 if !initialized.key?('trackers') && !initialized.key?('tracker_ids') | 128 if !initialized.key?('trackers') && !initialized.key?('tracker_ids') |
126 self.trackers = Tracker.sorted.all | 129 default = Setting.default_projects_tracker_ids |
130 if default.is_a?(Array) | |
131 self.trackers = Tracker.where(:id => default.map(&:to_i)).sorted.all | |
132 else | |
133 self.trackers = Tracker.sorted.all | |
134 end | |
127 end | 135 end |
128 end | 136 end |
129 | 137 |
130 def identifier=(identifier) | 138 def identifier=(identifier) |
131 super unless identifier_frozen? | 139 super unless identifier_frozen? |
136 end | 144 end |
137 | 145 |
138 # returns latest created projects | 146 # returns latest created projects |
139 # non public projects will be returned only if user is a member of those | 147 # non public projects will be returned only if user is a member of those |
140 def self.latest(user=nil, count=5) | 148 def self.latest(user=nil, count=5) |
141 visible(user).find(:all, :limit => count, :order => "created_on DESC") | 149 visible(user).limit(count).order("created_on DESC").all |
142 end | 150 end |
143 | 151 |
144 # Returns true if the project is visible to +user+ or to the current user. | 152 # Returns true if the project is visible to +user+ or to the current user. |
145 def visible?(user=User.current) | 153 def visible?(user=User.current) |
146 user.allowed_to?(:view_project, self) | 154 user.allowed_to?(:view_project, self) |
147 end | 155 end |
275 | 283 |
276 def self.find_by_param(*args) | 284 def self.find_by_param(*args) |
277 self.find(*args) | 285 self.find(*args) |
278 end | 286 end |
279 | 287 |
288 alias :base_reload :reload | |
280 def reload(*args) | 289 def reload(*args) |
281 @shared_versions = nil | 290 @shared_versions = nil |
282 @rolled_up_versions = nil | 291 @rolled_up_versions = nil |
283 @rolled_up_trackers = nil | 292 @rolled_up_trackers = nil |
284 @all_issue_custom_fields = nil | 293 @all_issue_custom_fields = nil |
285 @all_time_entry_custom_fields = nil | 294 @all_time_entry_custom_fields = nil |
286 @to_param = nil | 295 @to_param = nil |
287 @allowed_parents = nil | 296 @allowed_parents = nil |
288 @allowed_permissions = nil | 297 @allowed_permissions = nil |
289 @actions_allowed = nil | 298 @actions_allowed = nil |
290 super | 299 @start_date = nil |
300 @due_date = nil | |
301 base_reload(*args) | |
291 end | 302 end |
292 | 303 |
293 def to_param | 304 def to_param |
294 # id is used for projects with a numeric identifier (compatibility) | 305 # id is used for projects with a numeric identifier (compatibility) |
295 @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id.to_s : identifier) | 306 @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id.to_s : identifier) |
306 # Archives the project and its descendants | 317 # Archives the project and its descendants |
307 def archive | 318 def archive |
308 # Check that there is no issue of a non descendant project that is assigned | 319 # Check that there is no issue of a non descendant project that is assigned |
309 # to one of the project or descendant versions | 320 # to one of the project or descendant versions |
310 v_ids = self_and_descendants.collect {|p| p.version_ids}.flatten | 321 v_ids = self_and_descendants.collect {|p| p.version_ids}.flatten |
311 if v_ids.any? && Issue.find(:first, :include => :project, | 322 if v_ids.any? && |
312 :conditions => ["(#{Project.table_name}.lft < ? OR #{Project.table_name}.rgt > ?)" + | 323 Issue. |
313 " AND #{Issue.table_name}.fixed_version_id IN (?)", lft, rgt, v_ids]) | 324 includes(:project). |
325 where("#{Project.table_name}.lft < ? OR #{Project.table_name}.rgt > ?", lft, rgt). | |
326 where("#{Issue.table_name}.fixed_version_id IN (?)", v_ids). | |
327 exists? | |
314 return false | 328 return false |
315 end | 329 end |
316 Project.transaction do | 330 Project.transaction do |
317 archive! | 331 archive! |
318 end | 332 end |
336 | 350 |
337 # Returns an array of projects the project can be moved to | 351 # Returns an array of projects the project can be moved to |
338 # by the current user | 352 # by the current user |
339 def allowed_parents | 353 def allowed_parents |
340 return @allowed_parents if @allowed_parents | 354 return @allowed_parents if @allowed_parents |
341 @allowed_parents = Project.find(:all, :conditions => Project.allowed_to_condition(User.current, :add_subprojects)) | 355 @allowed_parents = Project.where(Project.allowed_to_condition(User.current, :add_subprojects)).all |
342 @allowed_parents = @allowed_parents - self_and_descendants | 356 @allowed_parents = @allowed_parents - self_and_descendants |
343 if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?) | 357 if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?) |
344 @allowed_parents << nil | 358 @allowed_parents << nil |
345 end | 359 end |
346 unless parent.nil? || @allowed_parents.empty? || @allowed_parents.include?(parent) | 360 unless parent.nil? || @allowed_parents.empty? || @allowed_parents.include?(parent) |
404 end | 418 end |
405 | 419 |
406 # Returns an array of the trackers used by the project and its active sub projects | 420 # Returns an array of the trackers used by the project and its active sub projects |
407 def rolled_up_trackers | 421 def rolled_up_trackers |
408 @rolled_up_trackers ||= | 422 @rolled_up_trackers ||= |
409 Tracker.find(:all, :joins => :projects, | 423 Tracker. |
410 :select => "DISTINCT #{Tracker.table_name}.*", | 424 joins(:projects). |
411 :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt], | 425 joins("JOIN #{EnabledModule.table_name} ON #{EnabledModule.table_name}.project_id = #{Project.table_name}.id AND #{EnabledModule.table_name}.name = 'issue_tracking'"). |
412 :order => "#{Tracker.table_name}.position") | 426 select("DISTINCT #{Tracker.table_name}.*"). |
427 where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt). | |
428 sorted. | |
429 all | |
413 end | 430 end |
414 | 431 |
415 # Closes open and locked project versions that are completed | 432 # Closes open and locked project versions that are completed |
416 def close_completed_versions | 433 def close_completed_versions |
417 Version.transaction do | 434 Version.transaction do |
418 versions.find(:all, :conditions => {:status => %w(open locked)}).each do |version| | 435 versions.where(:status => %w(open locked)).all.each do |version| |
419 if version.completed? | 436 if version.completed? |
420 version.update_attribute(:status, 'closed') | 437 version.update_attribute(:status, 'closed') |
421 end | 438 end |
422 end | 439 end |
423 end | 440 end |
450 end | 467 end |
451 end | 468 end |
452 | 469 |
453 # Returns a hash of project users grouped by role | 470 # Returns a hash of project users grouped by role |
454 def users_by_role | 471 def users_by_role |
455 members.find(:all, :include => [:user, :roles]).inject({}) do |h, m| | 472 members.includes(:user, :roles).all.inject({}) do |h, m| |
456 m.roles.each do |r| | 473 m.roles.each do |r| |
457 h[r] ||= [] | 474 h[r] ||= [] |
458 h[r] << m.user | 475 h[r] << m.user |
459 end | 476 end |
460 h | 477 h |
529 s | 546 s |
530 end | 547 end |
531 | 548 |
532 # The earliest start date of a project, based on it's issues and versions | 549 # The earliest start date of a project, based on it's issues and versions |
533 def start_date | 550 def start_date |
534 [ | 551 @start_date ||= [ |
535 issues.minimum('start_date'), | 552 issues.minimum('start_date'), |
536 shared_versions.collect(&:effective_date), | 553 shared_versions.minimum('effective_date'), |
537 shared_versions.collect(&:start_date) | 554 Issue.fixed_version(shared_versions).minimum('start_date') |
538 ].flatten.compact.min | 555 ].compact.min |
539 end | 556 end |
540 | 557 |
541 # The latest due date of an issue or version | 558 # The latest due date of an issue or version |
542 def due_date | 559 def due_date |
543 [ | 560 @due_date ||= [ |
544 issues.maximum('due_date'), | 561 issues.maximum('due_date'), |
545 shared_versions.collect(&:effective_date), | 562 shared_versions.maximum('effective_date'), |
546 shared_versions.collect {|v| v.fixed_issues.maximum('due_date')} | 563 Issue.fixed_version(shared_versions).maximum('due_date') |
547 ].flatten.compact.max | 564 ].compact.max |
548 end | 565 end |
549 | 566 |
550 def overdue? | 567 def overdue? |
551 active? && !due_date.nil? && (due_date < Date.today) | 568 active? && !due_date.nil? && (due_date < Date.today) |
552 end | 569 end |
558 total = self_and_descendants.collect(&:completed_percent).sum | 575 total = self_and_descendants.collect(&:completed_percent).sum |
559 | 576 |
560 total / self_and_descendants.count | 577 total / self_and_descendants.count |
561 else | 578 else |
562 if versions.count > 0 | 579 if versions.count > 0 |
563 total = versions.collect(&:completed_pourcent).sum | 580 total = versions.collect(&:completed_percent).sum |
564 | 581 |
565 total / versions.count | 582 total / versions.count |
566 else | 583 else |
567 100 | 584 100 |
568 end | 585 end |
640 'issue_custom_field_ids' | 657 'issue_custom_field_ids' |
641 | 658 |
642 safe_attributes 'enabled_module_names', | 659 safe_attributes 'enabled_module_names', |
643 :if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) } | 660 :if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) } |
644 | 661 |
662 safe_attributes 'inherit_members', | |
663 :if => lambda {|project, user| project.parent.nil? || project.parent.visible?(user)} | |
664 | |
645 # Returns an array of projects that are in this project's hierarchy | 665 # Returns an array of projects that are in this project's hierarchy |
646 # | 666 # |
647 # Example: parents, children, siblings | 667 # Example: parents, children, siblings |
648 def hierarchy | 668 def hierarchy |
649 parents = project.self_and_ancestors || [] | 669 parents = project.self_and_ancestors || [] |
651 project_hierarchy = parents | descendants # Set union | 671 project_hierarchy = parents | descendants # Set union |
652 end | 672 end |
653 | 673 |
654 # Returns an auto-generated project identifier based on the last identifier used | 674 # Returns an auto-generated project identifier based on the last identifier used |
655 def self.next_identifier | 675 def self.next_identifier |
656 p = Project.find(:first, :order => 'created_on DESC') | 676 p = Project.order('id DESC').first |
657 p.nil? ? nil : p.identifier.to_s.succ | 677 p.nil? ? nil : p.identifier.to_s.succ |
658 end | 678 end |
659 | 679 |
660 # Copies and saves the Project instance based on the +project+. | 680 # Copies and saves the Project instance based on the +project+. |
661 # Duplicates the source project's: | 681 # Duplicates the source project's: |
688 save | 708 save |
689 end | 709 end |
690 end | 710 end |
691 end | 711 end |
692 | 712 |
693 | 713 # Returns a new unsaved Project instance with attributes copied from +project+ |
694 # Copies +project+ and returns the new instance. This will not save | |
695 # the copy | |
696 def self.copy_from(project) | 714 def self.copy_from(project) |
697 begin | 715 project = project.is_a?(Project) ? project : Project.find(project) |
698 project = project.is_a?(Project) ? project : Project.find(project) | 716 # clear unique attributes |
699 if project | 717 attributes = project.attributes.dup.except('id', 'name', 'identifier', 'status', 'parent_id', 'lft', 'rgt') |
700 # clear unique attributes | 718 copy = Project.new(attributes) |
701 attributes = project.attributes.dup.except('id', 'name', 'identifier', 'status', 'parent_id', 'lft', 'rgt') | 719 copy.enabled_modules = project.enabled_modules |
702 copy = Project.new(attributes) | 720 copy.trackers = project.trackers |
703 copy.enabled_modules = project.enabled_modules | 721 copy.custom_values = project.custom_values.collect {|v| v.clone} |
704 copy.trackers = project.trackers | 722 copy.issue_custom_fields = project.issue_custom_fields |
705 copy.custom_values = project.custom_values.collect {|v| v.clone} | 723 copy |
706 copy.issue_custom_fields = project.issue_custom_fields | |
707 return copy | |
708 else | |
709 return nil | |
710 end | |
711 rescue ActiveRecord::RecordNotFound | |
712 return nil | |
713 end | |
714 end | 724 end |
715 | 725 |
716 # Yields the given block for each project with its level in the tree | 726 # Yields the given block for each project with its level in the tree |
717 def self.project_tree(projects, &block) | 727 def self.project_tree(projects, &block) |
718 ancestors = [] | 728 ancestors = [] |
724 ancestors << project | 734 ancestors << project |
725 end | 735 end |
726 end | 736 end |
727 | 737 |
728 private | 738 private |
739 | |
740 def after_parent_changed(parent_was) | |
741 remove_inherited_member_roles | |
742 add_inherited_member_roles | |
743 end | |
744 | |
745 def update_inherited_members | |
746 if parent | |
747 if inherit_members? && !inherit_members_was | |
748 remove_inherited_member_roles | |
749 add_inherited_member_roles | |
750 elsif !inherit_members? && inherit_members_was | |
751 remove_inherited_member_roles | |
752 end | |
753 end | |
754 end | |
755 | |
756 def remove_inherited_member_roles | |
757 member_roles = memberships.map(&:member_roles).flatten | |
758 member_role_ids = member_roles.map(&:id) | |
759 member_roles.each do |member_role| | |
760 if member_role.inherited_from && !member_role_ids.include?(member_role.inherited_from) | |
761 member_role.destroy | |
762 end | |
763 end | |
764 end | |
765 | |
766 def add_inherited_member_roles | |
767 if inherit_members? && parent | |
768 parent.memberships.each do |parent_member| | |
769 member = Member.find_or_new(self.id, parent_member.user_id) | |
770 parent_member.member_roles.each do |parent_member_role| | |
771 member.member_roles << MemberRole.new(:role => parent_member_role.role, :inherited_from => parent_member_role.id) | |
772 end | |
773 member.save! | |
774 end | |
775 end | |
776 end | |
729 | 777 |
730 # Copies wiki from +project+ | 778 # Copies wiki from +project+ |
731 def copy_wiki(project) | 779 def copy_wiki(project) |
732 # Check that the source project has a wiki first | 780 # Check that the source project has a wiki first |
733 unless project.wiki.nil? | 781 unless project.wiki.nil? |
786 version.update_attribute :status, 'open' | 834 version.update_attribute :status, 'open' |
787 end | 835 end |
788 | 836 |
789 # Get issues sorted by root_id, lft so that parent issues | 837 # Get issues sorted by root_id, lft so that parent issues |
790 # get copied before their children | 838 # get copied before their children |
791 project.issues.find(:all, :order => 'root_id, lft').each do |issue| | 839 project.issues.reorder('root_id, lft').all.each do |issue| |
792 new_issue = Issue.new | 840 new_issue = Issue.new |
793 new_issue.copy_from(issue, :subtasks => false, :link => false) | 841 new_issue.copy_from(issue, :subtasks => false, :link => false) |
794 new_issue.project = self | 842 new_issue.project = self |
843 # Changing project resets the custom field values | |
844 # TODO: handle this in Issue#project= | |
845 new_issue.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h} | |
795 # Reassign fixed_versions by name, since names are unique per project | 846 # Reassign fixed_versions by name, since names are unique per project |
796 if issue.fixed_version && issue.fixed_version.project == project | 847 if issue.fixed_version && issue.fixed_version.project == project |
797 new_issue.fixed_version = self.versions.detect {|v| v.name == issue.fixed_version.name} | 848 new_issue.fixed_version = self.versions.detect {|v| v.name == issue.fixed_version.name} |
798 end | 849 end |
799 # Reassign the category by name, since names are unique per project | 850 # Reassign the category by name, since names are unique per project |
872 end | 923 end |
873 | 924 |
874 # Copies queries from +project+ | 925 # Copies queries from +project+ |
875 def copy_queries(project) | 926 def copy_queries(project) |
876 project.queries.each do |query| | 927 project.queries.each do |query| |
877 new_query = ::Query.new | 928 new_query = IssueQuery.new |
878 new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria") | 929 new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria") |
879 new_query.sort_criteria = query.sort_criteria if query.sort_criteria | 930 new_query.sort_criteria = query.sort_criteria if query.sort_criteria |
880 new_query.project = self | 931 new_query.project = self |
881 new_query.user_id = query.user_id | 932 new_query.user_id = query.user_id |
882 self.queries << new_query | 933 self.queries << new_query |
929 | 980 |
930 # Returns the systemwide active activities merged with the project specific overrides | 981 # Returns the systemwide active activities merged with the project specific overrides |
931 def system_activities_and_project_overrides(include_inactive=false) | 982 def system_activities_and_project_overrides(include_inactive=false) |
932 if include_inactive | 983 if include_inactive |
933 return TimeEntryActivity.shared. | 984 return TimeEntryActivity.shared. |
934 find(:all, | 985 where("id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)).all + |
935 :conditions => ["id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)]) + | |
936 self.time_entry_activities | 986 self.time_entry_activities |
937 else | 987 else |
938 return TimeEntryActivity.shared.active. | 988 return TimeEntryActivity.shared.active. |
939 find(:all, | 989 where("id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)).all + |
940 :conditions => ["id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)]) + | |
941 self.time_entry_activities.active | 990 self.time_entry_activities.active |
942 end | 991 end |
943 end | 992 end |
944 | 993 |
945 # Archives subprojects recursively | 994 # Archives subprojects recursively |
954 set_or_update_position_under(parent) | 1003 set_or_update_position_under(parent) |
955 end | 1004 end |
956 | 1005 |
957 # Inserts/moves the project so that target's children or root projects stay alphabetically sorted | 1006 # Inserts/moves the project so that target's children or root projects stay alphabetically sorted |
958 def set_or_update_position_under(target_parent) | 1007 def set_or_update_position_under(target_parent) |
1008 parent_was = parent | |
959 sibs = (target_parent.nil? ? self.class.roots : target_parent.children) | 1009 sibs = (target_parent.nil? ? self.class.roots : target_parent.children) |
960 to_be_inserted_before = sibs.sort_by {|c| c.name.to_s.downcase}.detect {|c| c.name.to_s.downcase > name.to_s.downcase } | 1010 to_be_inserted_before = sibs.sort_by {|c| c.name.to_s.downcase}.detect {|c| c.name.to_s.downcase > name.to_s.downcase } |
961 | 1011 |
962 if to_be_inserted_before | 1012 if to_be_inserted_before |
963 move_to_left_of(to_be_inserted_before) | 1013 move_to_left_of(to_be_inserted_before) |
970 end | 1020 end |
971 else | 1021 else |
972 # move_to_child_of adds the project in last (ie.right) position | 1022 # move_to_child_of adds the project in last (ie.right) position |
973 move_to_child_of(target_parent) | 1023 move_to_child_of(target_parent) |
974 end | 1024 end |
1025 if parent_was != target_parent | |
1026 after_parent_changed(parent_was) | |
1027 end | |
975 end | 1028 end |
976 end | 1029 end |