Mercurial > hg > soundsoftware-site
comparison app/models/project.rb @ 1298:4f746d8966dd redmine_2.3_integration
Merge from redmine-2.3 branch to create new branch redmine-2.3-integration
author | Chris Cannam |
---|---|
date | Fri, 14 Jun 2013 09:28:30 +0100 |
parents | 0a574315af3e 622f24f53b42 |
children |
comparison
equal
deleted
inserted
replaced
1297:0a574315af3e | 1298:4f746d8966dd |
---|---|
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) } | |
92 scope :visible_roots, lambda { { :conditions => Project.root_visible_by(User.current) } } | 94 scope :visible_roots, lambda { { :conditions => Project.root_visible_by(User.current) } } |
95 scope :visible, lambda {|*args| where(Project.visible_condition(args.shift || User.current, *args)) } | |
93 scope :allowed_to, lambda {|*args| | 96 scope :allowed_to, lambda {|*args| |
94 user = User.current | 97 user = User.current |
95 permission = nil | 98 permission = nil |
96 if args.first.is_a?(Symbol) | 99 if args.first.is_a?(Symbol) |
97 permission = args.shift | 100 permission = args.shift |
98 else | 101 else |
99 user = args.shift | 102 user = args.shift |
100 permission = args.shift | 103 permission = args.shift |
101 end | 104 end |
102 { :conditions => Project.allowed_to_condition(user, permission, *args) } | 105 where(Project.allowed_to_condition(user, permission, *args)) |
103 } | 106 } |
104 scope :like, lambda {|arg| | 107 scope :like, lambda {|arg| |
105 if arg.blank? | 108 if arg.blank? |
106 {} | 109 where(nil) |
107 else | 110 else |
108 pattern = "%#{arg.to_s.strip.downcase}%" | 111 pattern = "%#{arg.to_s.strip.downcase}%" |
109 {:conditions => ["LOWER(identifier) LIKE :p OR LOWER(name) LIKE :p", {:p => pattern}]} | 112 where("LOWER(identifier) LIKE :p OR LOWER(name) LIKE :p", :p => pattern) |
110 end | 113 end |
111 } | 114 } |
112 | 115 |
113 def initialize(attributes=nil, *args) | 116 def initialize(attributes=nil, *args) |
114 super | 117 super |
122 end | 125 end |
123 if !initialized.key?('enabled_module_names') | 126 if !initialized.key?('enabled_module_names') |
124 self.enabled_module_names = Setting.default_projects_modules | 127 self.enabled_module_names = Setting.default_projects_modules |
125 end | 128 end |
126 if !initialized.key?('trackers') && !initialized.key?('tracker_ids') | 129 if !initialized.key?('trackers') && !initialized.key?('tracker_ids') |
127 self.trackers = Tracker.sorted.all | 130 default = Setting.default_projects_tracker_ids |
131 if default.is_a?(Array) | |
132 self.trackers = Tracker.where(:id => default.map(&:to_i)).sorted.all | |
133 else | |
134 self.trackers = Tracker.sorted.all | |
135 end | |
128 end | 136 end |
129 end | 137 end |
130 | 138 |
131 def identifier=(identifier) | 139 def identifier=(identifier) |
132 super unless identifier_frozen? | 140 super unless identifier_frozen? |
137 end | 145 end |
138 | 146 |
139 # returns latest created projects | 147 # returns latest created projects |
140 # non public projects will be returned only if user is a member of those | 148 # non public projects will be returned only if user is a member of those |
141 def self.latest(user=nil, count=5) | 149 def self.latest(user=nil, count=5) |
142 visible(user).find(:all, :limit => count, :order => "created_on DESC") | 150 visible(user).limit(count).order("created_on DESC").all |
143 end | 151 end |
144 | 152 |
145 # Returns true if the project is visible to +user+ or to the current user. | 153 # Returns true if the project is visible to +user+ or to the current user. |
146 def visible?(user=User.current) | 154 def visible?(user=User.current) |
147 user.allowed_to?(:view_project, self) | 155 user.allowed_to?(:view_project, self) |
148 end | 156 end |
280 | 288 |
281 def self.find_by_param(*args) | 289 def self.find_by_param(*args) |
282 self.find(*args) | 290 self.find(*args) |
283 end | 291 end |
284 | 292 |
293 alias :base_reload :reload | |
285 def reload(*args) | 294 def reload(*args) |
286 @shared_versions = nil | 295 @shared_versions = nil |
287 @rolled_up_versions = nil | 296 @rolled_up_versions = nil |
288 @rolled_up_trackers = nil | 297 @rolled_up_trackers = nil |
289 @all_issue_custom_fields = nil | 298 @all_issue_custom_fields = nil |
290 @all_time_entry_custom_fields = nil | 299 @all_time_entry_custom_fields = nil |
291 @to_param = nil | 300 @to_param = nil |
292 @allowed_parents = nil | 301 @allowed_parents = nil |
293 @allowed_permissions = nil | 302 @allowed_permissions = nil |
294 @actions_allowed = nil | 303 @actions_allowed = nil |
295 super | 304 @start_date = nil |
305 @due_date = nil | |
306 base_reload(*args) | |
296 end | 307 end |
297 | 308 |
298 def to_param | 309 def to_param |
299 # id is used for projects with a numeric identifier (compatibility) | 310 # id is used for projects with a numeric identifier (compatibility) |
300 @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id.to_s : identifier) | 311 @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id.to_s : identifier) |
311 # Archives the project and its descendants | 322 # Archives the project and its descendants |
312 def archive | 323 def archive |
313 # Check that there is no issue of a non descendant project that is assigned | 324 # Check that there is no issue of a non descendant project that is assigned |
314 # to one of the project or descendant versions | 325 # to one of the project or descendant versions |
315 v_ids = self_and_descendants.collect {|p| p.version_ids}.flatten | 326 v_ids = self_and_descendants.collect {|p| p.version_ids}.flatten |
316 if v_ids.any? && Issue.find(:first, :include => :project, | 327 if v_ids.any? && |
317 :conditions => ["(#{Project.table_name}.lft < ? OR #{Project.table_name}.rgt > ?)" + | 328 Issue. |
318 " AND #{Issue.table_name}.fixed_version_id IN (?)", lft, rgt, v_ids]) | 329 includes(:project). |
330 where("#{Project.table_name}.lft < ? OR #{Project.table_name}.rgt > ?", lft, rgt). | |
331 where("#{Issue.table_name}.fixed_version_id IN (?)", v_ids). | |
332 exists? | |
319 return false | 333 return false |
320 end | 334 end |
321 Project.transaction do | 335 Project.transaction do |
322 archive! | 336 archive! |
323 end | 337 end |
341 | 355 |
342 # Returns an array of projects the project can be moved to | 356 # Returns an array of projects the project can be moved to |
343 # by the current user | 357 # by the current user |
344 def allowed_parents | 358 def allowed_parents |
345 return @allowed_parents if @allowed_parents | 359 return @allowed_parents if @allowed_parents |
346 @allowed_parents = Project.find(:all, :conditions => Project.allowed_to_condition(User.current, :add_subprojects)) | 360 @allowed_parents = Project.where(Project.allowed_to_condition(User.current, :add_subprojects)).all |
347 @allowed_parents = @allowed_parents - self_and_descendants | 361 @allowed_parents = @allowed_parents - self_and_descendants |
348 if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?) | 362 if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?) |
349 @allowed_parents << nil | 363 @allowed_parents << nil |
350 end | 364 end |
351 unless parent.nil? || @allowed_parents.empty? || @allowed_parents.include?(parent) | 365 unless parent.nil? || @allowed_parents.empty? || @allowed_parents.include?(parent) |
409 end | 423 end |
410 | 424 |
411 # Returns an array of the trackers used by the project and its active sub projects | 425 # Returns an array of the trackers used by the project and its active sub projects |
412 def rolled_up_trackers | 426 def rolled_up_trackers |
413 @rolled_up_trackers ||= | 427 @rolled_up_trackers ||= |
414 Tracker.find(:all, :joins => :projects, | 428 Tracker. |
415 :select => "DISTINCT #{Tracker.table_name}.*", | 429 joins(:projects). |
416 :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt], | 430 joins("JOIN #{EnabledModule.table_name} ON #{EnabledModule.table_name}.project_id = #{Project.table_name}.id AND #{EnabledModule.table_name}.name = 'issue_tracking'"). |
417 :order => "#{Tracker.table_name}.position") | 431 select("DISTINCT #{Tracker.table_name}.*"). |
432 where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt). | |
433 sorted. | |
434 all | |
418 end | 435 end |
419 | 436 |
420 # Closes open and locked project versions that are completed | 437 # Closes open and locked project versions that are completed |
421 def close_completed_versions | 438 def close_completed_versions |
422 Version.transaction do | 439 Version.transaction do |
423 versions.find(:all, :conditions => {:status => %w(open locked)}).each do |version| | 440 versions.where(:status => %w(open locked)).all.each do |version| |
424 if version.completed? | 441 if version.completed? |
425 version.update_attribute(:status, 'closed') | 442 version.update_attribute(:status, 'closed') |
426 end | 443 end |
427 end | 444 end |
428 end | 445 end |
455 end | 472 end |
456 end | 473 end |
457 | 474 |
458 # Returns a hash of project users grouped by role | 475 # Returns a hash of project users grouped by role |
459 def users_by_role | 476 def users_by_role |
460 members.find(:all, :include => [:user, :roles]).inject({}) do |h, m| | 477 members.includes(:user, :roles).all.inject({}) do |h, m| |
461 m.roles.each do |r| | 478 m.roles.each do |r| |
462 h[r] ||= [] | 479 h[r] ||= [] |
463 h[r] << m.user | 480 h[r] << m.user |
464 end | 481 end |
465 h | 482 h |
548 s | 565 s |
549 end | 566 end |
550 | 567 |
551 # The earliest start date of a project, based on it's issues and versions | 568 # The earliest start date of a project, based on it's issues and versions |
552 def start_date | 569 def start_date |
553 [ | 570 @start_date ||= [ |
554 issues.minimum('start_date'), | 571 issues.minimum('start_date'), |
555 shared_versions.collect(&:effective_date), | 572 shared_versions.minimum('effective_date'), |
556 shared_versions.collect(&:start_date) | 573 Issue.fixed_version(shared_versions).minimum('start_date') |
557 ].flatten.compact.min | 574 ].compact.min |
558 end | 575 end |
559 | 576 |
560 # The latest due date of an issue or version | 577 # The latest due date of an issue or version |
561 def due_date | 578 def due_date |
562 [ | 579 @due_date ||= [ |
563 issues.maximum('due_date'), | 580 issues.maximum('due_date'), |
564 shared_versions.collect(&:effective_date), | 581 shared_versions.maximum('effective_date'), |
565 shared_versions.collect {|v| v.fixed_issues.maximum('due_date')} | 582 Issue.fixed_version(shared_versions).maximum('due_date') |
566 ].flatten.compact.max | 583 ].compact.max |
567 end | 584 end |
568 | 585 |
569 def overdue? | 586 def overdue? |
570 active? && !due_date.nil? && (due_date < Date.today) | 587 active? && !due_date.nil? && (due_date < Date.today) |
571 end | 588 end |
577 total = self_and_descendants.collect(&:completed_percent).sum | 594 total = self_and_descendants.collect(&:completed_percent).sum |
578 | 595 |
579 total / self_and_descendants.count | 596 total / self_and_descendants.count |
580 else | 597 else |
581 if versions.count > 0 | 598 if versions.count > 0 |
582 total = versions.collect(&:completed_pourcent).sum | 599 total = versions.collect(&:completed_percent).sum |
583 | 600 |
584 total / versions.count | 601 total / versions.count |
585 else | 602 else |
586 100 | 603 100 |
587 end | 604 end |
660 'has_welcome_page' | 677 'has_welcome_page' |
661 | 678 |
662 safe_attributes 'enabled_module_names', | 679 safe_attributes 'enabled_module_names', |
663 :if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) } | 680 :if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) } |
664 | 681 |
682 safe_attributes 'inherit_members', | |
683 :if => lambda {|project, user| project.parent.nil? || project.parent.visible?(user)} | |
684 | |
665 # Returns an array of projects that are in this project's hierarchy | 685 # Returns an array of projects that are in this project's hierarchy |
666 # | 686 # |
667 # Example: parents, children, siblings | 687 # Example: parents, children, siblings |
668 def hierarchy | 688 def hierarchy |
669 parents = project.self_and_ancestors || [] | 689 parents = project.self_and_ancestors || [] |
671 project_hierarchy = parents | descendants # Set union | 691 project_hierarchy = parents | descendants # Set union |
672 end | 692 end |
673 | 693 |
674 # Returns an auto-generated project identifier based on the last identifier used | 694 # Returns an auto-generated project identifier based on the last identifier used |
675 def self.next_identifier | 695 def self.next_identifier |
676 p = Project.find(:first, :order => 'created_on DESC') | 696 p = Project.order('id DESC').first |
677 p.nil? ? nil : p.identifier.to_s.succ | 697 p.nil? ? nil : p.identifier.to_s.succ |
678 end | 698 end |
679 | 699 |
680 # Copies and saves the Project instance based on the +project+. | 700 # Copies and saves the Project instance based on the +project+. |
681 # Duplicates the source project's: | 701 # Duplicates the source project's: |
708 save | 728 save |
709 end | 729 end |
710 end | 730 end |
711 end | 731 end |
712 | 732 |
713 | 733 # Returns a new unsaved Project instance with attributes copied from +project+ |
714 # Copies +project+ and returns the new instance. This will not save | |
715 # the copy | |
716 def self.copy_from(project) | 734 def self.copy_from(project) |
717 begin | 735 project = project.is_a?(Project) ? project : Project.find(project) |
718 project = project.is_a?(Project) ? project : Project.find(project) | 736 # clear unique attributes |
719 if project | 737 attributes = project.attributes.dup.except('id', 'name', 'identifier', 'status', 'parent_id', 'lft', 'rgt') |
720 # clear unique attributes | 738 copy = Project.new(attributes) |
721 attributes = project.attributes.dup.except('id', 'name', 'identifier', 'status', 'parent_id', 'lft', 'rgt') | 739 copy.enabled_modules = project.enabled_modules |
722 copy = Project.new(attributes) | 740 copy.trackers = project.trackers |
723 copy.enabled_modules = project.enabled_modules | 741 copy.custom_values = project.custom_values.collect {|v| v.clone} |
724 copy.trackers = project.trackers | 742 copy.issue_custom_fields = project.issue_custom_fields |
725 copy.custom_values = project.custom_values.collect {|v| v.clone} | 743 copy |
726 copy.issue_custom_fields = project.issue_custom_fields | |
727 return copy | |
728 else | |
729 return nil | |
730 end | |
731 rescue ActiveRecord::RecordNotFound | |
732 return nil | |
733 end | |
734 end | 744 end |
735 | 745 |
736 # Yields the given block for each project with its level in the tree | 746 # Yields the given block for each project with its level in the tree |
737 def self.project_tree(projects, &block) | 747 def self.project_tree(projects, &block) |
738 ancestors = [] | 748 ancestors = [] |
744 ancestors << project | 754 ancestors << project |
745 end | 755 end |
746 end | 756 end |
747 | 757 |
748 private | 758 private |
759 | |
760 def after_parent_changed(parent_was) | |
761 remove_inherited_member_roles | |
762 add_inherited_member_roles | |
763 end | |
764 | |
765 def update_inherited_members | |
766 if parent | |
767 if inherit_members? && !inherit_members_was | |
768 remove_inherited_member_roles | |
769 add_inherited_member_roles | |
770 elsif !inherit_members? && inherit_members_was | |
771 remove_inherited_member_roles | |
772 end | |
773 end | |
774 end | |
775 | |
776 def remove_inherited_member_roles | |
777 member_roles = memberships.map(&:member_roles).flatten | |
778 member_role_ids = member_roles.map(&:id) | |
779 member_roles.each do |member_role| | |
780 if member_role.inherited_from && !member_role_ids.include?(member_role.inherited_from) | |
781 member_role.destroy | |
782 end | |
783 end | |
784 end | |
785 | |
786 def add_inherited_member_roles | |
787 if inherit_members? && parent | |
788 parent.memberships.each do |parent_member| | |
789 member = Member.find_or_new(self.id, parent_member.user_id) | |
790 parent_member.member_roles.each do |parent_member_role| | |
791 member.member_roles << MemberRole.new(:role => parent_member_role.role, :inherited_from => parent_member_role.id) | |
792 end | |
793 member.save! | |
794 end | |
795 end | |
796 end | |
749 | 797 |
750 # Copies wiki from +project+ | 798 # Copies wiki from +project+ |
751 def copy_wiki(project) | 799 def copy_wiki(project) |
752 # Check that the source project has a wiki first | 800 # Check that the source project has a wiki first |
753 unless project.wiki.nil? | 801 unless project.wiki.nil? |
806 version.update_attribute :status, 'open' | 854 version.update_attribute :status, 'open' |
807 end | 855 end |
808 | 856 |
809 # Get issues sorted by root_id, lft so that parent issues | 857 # Get issues sorted by root_id, lft so that parent issues |
810 # get copied before their children | 858 # get copied before their children |
811 project.issues.find(:all, :order => 'root_id, lft').each do |issue| | 859 project.issues.reorder('root_id, lft').all.each do |issue| |
812 new_issue = Issue.new | 860 new_issue = Issue.new |
813 new_issue.copy_from(issue, :subtasks => false, :link => false) | 861 new_issue.copy_from(issue, :subtasks => false, :link => false) |
814 new_issue.project = self | 862 new_issue.project = self |
863 # Changing project resets the custom field values | |
864 # TODO: handle this in Issue#project= | |
865 new_issue.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h} | |
815 # Reassign fixed_versions by name, since names are unique per project | 866 # Reassign fixed_versions by name, since names are unique per project |
816 if issue.fixed_version && issue.fixed_version.project == project | 867 if issue.fixed_version && issue.fixed_version.project == project |
817 new_issue.fixed_version = self.versions.detect {|v| v.name == issue.fixed_version.name} | 868 new_issue.fixed_version = self.versions.detect {|v| v.name == issue.fixed_version.name} |
818 end | 869 end |
819 # Reassign the category by name, since names are unique per project | 870 # Reassign the category by name, since names are unique per project |
892 end | 943 end |
893 | 944 |
894 # Copies queries from +project+ | 945 # Copies queries from +project+ |
895 def copy_queries(project) | 946 def copy_queries(project) |
896 project.queries.each do |query| | 947 project.queries.each do |query| |
897 new_query = ::Query.new | 948 new_query = IssueQuery.new |
898 new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria") | 949 new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria") |
899 new_query.sort_criteria = query.sort_criteria if query.sort_criteria | 950 new_query.sort_criteria = query.sort_criteria if query.sort_criteria |
900 new_query.project = self | 951 new_query.project = self |
901 new_query.user_id = query.user_id | 952 new_query.user_id = query.user_id |
902 self.queries << new_query | 953 self.queries << new_query |
949 | 1000 |
950 # Returns the systemwide active activities merged with the project specific overrides | 1001 # Returns the systemwide active activities merged with the project specific overrides |
951 def system_activities_and_project_overrides(include_inactive=false) | 1002 def system_activities_and_project_overrides(include_inactive=false) |
952 if include_inactive | 1003 if include_inactive |
953 return TimeEntryActivity.shared. | 1004 return TimeEntryActivity.shared. |
954 find(:all, | 1005 where("id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)).all + |
955 :conditions => ["id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)]) + | |
956 self.time_entry_activities | 1006 self.time_entry_activities |
957 else | 1007 else |
958 return TimeEntryActivity.shared.active. | 1008 return TimeEntryActivity.shared.active. |
959 find(:all, | 1009 where("id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)).all + |
960 :conditions => ["id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)]) + | |
961 self.time_entry_activities.active | 1010 self.time_entry_activities.active |
962 end | 1011 end |
963 end | 1012 end |
964 | 1013 |
965 # Archives subprojects recursively | 1014 # Archives subprojects recursively |
974 set_or_update_position_under(parent) | 1023 set_or_update_position_under(parent) |
975 end | 1024 end |
976 | 1025 |
977 # Inserts/moves the project so that target's children or root projects stay alphabetically sorted | 1026 # Inserts/moves the project so that target's children or root projects stay alphabetically sorted |
978 def set_or_update_position_under(target_parent) | 1027 def set_or_update_position_under(target_parent) |
1028 parent_was = parent | |
979 sibs = (target_parent.nil? ? self.class.roots : target_parent.children) | 1029 sibs = (target_parent.nil? ? self.class.roots : target_parent.children) |
980 to_be_inserted_before = sibs.sort_by {|c| c.name.to_s.downcase}.detect {|c| c.name.to_s.downcase > name.to_s.downcase } | 1030 to_be_inserted_before = sibs.sort_by {|c| c.name.to_s.downcase}.detect {|c| c.name.to_s.downcase > name.to_s.downcase } |
981 | 1031 |
982 if to_be_inserted_before | 1032 if to_be_inserted_before |
983 move_to_left_of(to_be_inserted_before) | 1033 move_to_left_of(to_be_inserted_before) |
990 end | 1040 end |
991 else | 1041 else |
992 # move_to_child_of adds the project in last (ie.right) position | 1042 # move_to_child_of adds the project in last (ie.right) position |
993 move_to_child_of(target_parent) | 1043 move_to_child_of(target_parent) |
994 end | 1044 end |
1045 if parent_was != target_parent | |
1046 after_parent_changed(parent_was) | |
1047 end | |
995 end | 1048 end |
996 end | 1049 end |