Mercurial > hg > soundsoftware-site
comparison app/models/issue_relation.rb @ 1115:433d4f72a19b redmine-2.2
Update to Redmine SVN revision 11137 on 2.2-stable branch
author | Chris Cannam |
---|---|
date | Mon, 07 Jan 2013 12:01:42 +0000 |
parents | cbb26bc654de |
children | 622f24f53b42 261b3d9a4903 |
comparison
equal
deleted
inserted
replaced
929:5f33065ddc4b | 1115:433d4f72a19b |
---|---|
1 # Redmine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006-2011 Jean-Philippe Lang | 2 # Copyright (C) 2006-2012 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. |
13 # | 13 # |
14 # You should have received a copy of the GNU General Public License | 14 # You should have received a copy of the GNU General Public License |
15 # along with this program; if not, write to the Free Software | 15 # along with this program; if not, write to the Free Software |
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | 17 |
18 # Class used to represent the relations of an issue | |
19 class IssueRelations < Array | |
20 include Redmine::I18n | |
21 | |
22 def initialize(issue, *args) | |
23 @issue = issue | |
24 super(*args) | |
25 end | |
26 | |
27 def to_s(*args) | |
28 map {|relation| "#{l(relation.label_for(@issue))} ##{relation.other_issue(@issue).id}"}.join(', ') | |
29 end | |
30 end | |
31 | |
18 class IssueRelation < ActiveRecord::Base | 32 class IssueRelation < ActiveRecord::Base |
19 belongs_to :issue_from, :class_name => 'Issue', :foreign_key => 'issue_from_id' | 33 belongs_to :issue_from, :class_name => 'Issue', :foreign_key => 'issue_from_id' |
20 belongs_to :issue_to, :class_name => 'Issue', :foreign_key => 'issue_to_id' | 34 belongs_to :issue_to, :class_name => 'Issue', :foreign_key => 'issue_to_id' |
21 | 35 |
22 TYPE_RELATES = "relates" | 36 TYPE_RELATES = "relates" |
24 TYPE_DUPLICATED = "duplicated" | 38 TYPE_DUPLICATED = "duplicated" |
25 TYPE_BLOCKS = "blocks" | 39 TYPE_BLOCKS = "blocks" |
26 TYPE_BLOCKED = "blocked" | 40 TYPE_BLOCKED = "blocked" |
27 TYPE_PRECEDES = "precedes" | 41 TYPE_PRECEDES = "precedes" |
28 TYPE_FOLLOWS = "follows" | 42 TYPE_FOLLOWS = "follows" |
43 TYPE_COPIED_TO = "copied_to" | |
44 TYPE_COPIED_FROM = "copied_from" | |
29 | 45 |
30 TYPES = { TYPE_RELATES => { :name => :label_relates_to, :sym_name => :label_relates_to, :order => 1, :sym => TYPE_RELATES }, | 46 TYPES = { |
31 TYPE_DUPLICATES => { :name => :label_duplicates, :sym_name => :label_duplicated_by, :order => 2, :sym => TYPE_DUPLICATED }, | 47 TYPE_RELATES => { :name => :label_relates_to, :sym_name => :label_relates_to, |
32 TYPE_DUPLICATED => { :name => :label_duplicated_by, :sym_name => :label_duplicates, :order => 3, :sym => TYPE_DUPLICATES, :reverse => TYPE_DUPLICATES }, | 48 :order => 1, :sym => TYPE_RELATES }, |
33 TYPE_BLOCKS => { :name => :label_blocks, :sym_name => :label_blocked_by, :order => 4, :sym => TYPE_BLOCKED }, | 49 TYPE_DUPLICATES => { :name => :label_duplicates, :sym_name => :label_duplicated_by, |
34 TYPE_BLOCKED => { :name => :label_blocked_by, :sym_name => :label_blocks, :order => 5, :sym => TYPE_BLOCKS, :reverse => TYPE_BLOCKS }, | 50 :order => 2, :sym => TYPE_DUPLICATED }, |
35 TYPE_PRECEDES => { :name => :label_precedes, :sym_name => :label_follows, :order => 6, :sym => TYPE_FOLLOWS }, | 51 TYPE_DUPLICATED => { :name => :label_duplicated_by, :sym_name => :label_duplicates, |
36 TYPE_FOLLOWS => { :name => :label_follows, :sym_name => :label_precedes, :order => 7, :sym => TYPE_PRECEDES, :reverse => TYPE_PRECEDES } | 52 :order => 3, :sym => TYPE_DUPLICATES, :reverse => TYPE_DUPLICATES }, |
37 }.freeze | 53 TYPE_BLOCKS => { :name => :label_blocks, :sym_name => :label_blocked_by, |
54 :order => 4, :sym => TYPE_BLOCKED }, | |
55 TYPE_BLOCKED => { :name => :label_blocked_by, :sym_name => :label_blocks, | |
56 :order => 5, :sym => TYPE_BLOCKS, :reverse => TYPE_BLOCKS }, | |
57 TYPE_PRECEDES => { :name => :label_precedes, :sym_name => :label_follows, | |
58 :order => 6, :sym => TYPE_FOLLOWS }, | |
59 TYPE_FOLLOWS => { :name => :label_follows, :sym_name => :label_precedes, | |
60 :order => 7, :sym => TYPE_PRECEDES, :reverse => TYPE_PRECEDES }, | |
61 TYPE_COPIED_TO => { :name => :label_copied_to, :sym_name => :label_copied_from, | |
62 :order => 8, :sym => TYPE_COPIED_FROM }, | |
63 TYPE_COPIED_FROM => { :name => :label_copied_from, :sym_name => :label_copied_to, | |
64 :order => 9, :sym => TYPE_COPIED_TO, :reverse => TYPE_COPIED_TO } | |
65 }.freeze | |
38 | 66 |
39 validates_presence_of :issue_from, :issue_to, :relation_type | 67 validates_presence_of :issue_from, :issue_to, :relation_type |
40 validates_inclusion_of :relation_type, :in => TYPES.keys | 68 validates_inclusion_of :relation_type, :in => TYPES.keys |
41 validates_numericality_of :delay, :allow_nil => true | 69 validates_numericality_of :delay, :allow_nil => true |
42 validates_uniqueness_of :issue_to_id, :scope => :issue_from_id | 70 validates_uniqueness_of :issue_to_id, :scope => :issue_from_id |
43 | |
44 validate :validate_issue_relation | 71 validate :validate_issue_relation |
45 | 72 |
46 attr_protected :issue_from_id, :issue_to_id | 73 attr_protected :issue_from_id, :issue_to_id |
47 | |
48 before_save :handle_issue_order | 74 before_save :handle_issue_order |
49 | 75 |
50 def visible?(user=User.current) | 76 def visible?(user=User.current) |
51 (issue_from.nil? || issue_from.visible?(user)) && (issue_to.nil? || issue_to.visible?(user)) | 77 (issue_from.nil? || issue_from.visible?(user)) && (issue_to.nil? || issue_to.visible?(user)) |
52 end | 78 end |
55 visible?(user) && | 81 visible?(user) && |
56 ((issue_from.nil? || user.allowed_to?(:manage_issue_relations, issue_from.project)) || | 82 ((issue_from.nil? || user.allowed_to?(:manage_issue_relations, issue_from.project)) || |
57 (issue_to.nil? || user.allowed_to?(:manage_issue_relations, issue_to.project))) | 83 (issue_to.nil? || user.allowed_to?(:manage_issue_relations, issue_to.project))) |
58 end | 84 end |
59 | 85 |
60 def after_initialize | 86 def initialize(attributes=nil, *args) |
87 super | |
61 if new_record? | 88 if new_record? |
62 if relation_type.blank? | 89 if relation_type.blank? |
63 self.relation_type = IssueRelation::TYPE_RELATES | 90 self.relation_type = IssueRelation::TYPE_RELATES |
64 end | 91 end |
65 end | 92 end |
66 end | 93 end |
67 | 94 |
68 def validate_issue_relation | 95 def validate_issue_relation |
69 if issue_from && issue_to | 96 if issue_from && issue_to |
70 errors.add :issue_to_id, :invalid if issue_from_id == issue_to_id | 97 errors.add :issue_to_id, :invalid if issue_from_id == issue_to_id |
71 errors.add :issue_to_id, :not_same_project unless issue_from.project_id == issue_to.project_id || Setting.cross_project_issue_relations? | 98 unless issue_from.project_id == issue_to.project_id || |
72 #detect circular dependencies depending wether the relation should be reversed | 99 Setting.cross_project_issue_relations? |
100 errors.add :issue_to_id, :not_same_project | |
101 end | |
102 # detect circular dependencies depending wether the relation should be reversed | |
73 if TYPES.has_key?(relation_type) && TYPES[relation_type][:reverse] | 103 if TYPES.has_key?(relation_type) && TYPES[relation_type][:reverse] |
74 errors.add :base, :circular_dependency if issue_from.all_dependent_issues.include? issue_to | 104 errors.add :base, :circular_dependency if issue_from.all_dependent_issues.include? issue_to |
75 else | 105 else |
76 errors.add :base, :circular_dependency if issue_to.all_dependent_issues.include? issue_from | 106 errors.add :base, :circular_dependency if issue_to.all_dependent_issues.include? issue_from |
77 end | 107 end |
78 errors.add :base, :cant_link_an_issue_with_a_descendant if issue_from.is_descendant_of?(issue_to) || issue_from.is_ancestor_of?(issue_to) | 108 if issue_from.is_descendant_of?(issue_to) || issue_from.is_ancestor_of?(issue_to) |
109 errors.add :base, :cant_link_an_issue_with_a_descendant | |
110 end | |
79 end | 111 end |
80 end | 112 end |
81 | 113 |
82 def other_issue(issue) | 114 def other_issue(issue) |
83 (self.issue_from_id == issue.id) ? issue_to : issue_from | 115 (self.issue_from_id == issue.id) ? issue_to : issue_from |
93 end | 125 end |
94 end | 126 end |
95 end | 127 end |
96 | 128 |
97 def label_for(issue) | 129 def label_for(issue) |
98 TYPES[relation_type] ? TYPES[relation_type][(self.issue_from_id == issue.id) ? :name : :sym_name] : :unknow | 130 TYPES[relation_type] ? |
131 TYPES[relation_type][(self.issue_from_id == issue.id) ? :name : :sym_name] : | |
132 :unknow | |
133 end | |
134 | |
135 def css_classes_for(issue) | |
136 "rel-#{relation_type_for(issue)}" | |
99 end | 137 end |
100 | 138 |
101 def handle_issue_order | 139 def handle_issue_order |
102 reverse_if_needed | 140 reverse_if_needed |
103 | 141 |
110 end | 148 end |
111 | 149 |
112 def set_issue_to_dates | 150 def set_issue_to_dates |
113 soonest_start = self.successor_soonest_start | 151 soonest_start = self.successor_soonest_start |
114 if soonest_start && issue_to | 152 if soonest_start && issue_to |
115 issue_to.reschedule_after(soonest_start) | 153 issue_to.reschedule_on!(soonest_start) |
116 end | 154 end |
117 end | 155 end |
118 | 156 |
119 def successor_soonest_start | 157 def successor_soonest_start |
120 if (TYPE_PRECEDES == self.relation_type) && delay && issue_from && (issue_from.start_date || issue_from.due_date) | 158 if (TYPE_PRECEDES == self.relation_type) && delay && issue_from && |
159 (issue_from.start_date || issue_from.due_date) | |
121 (issue_from.due_date || issue_from.start_date) + 1 + delay | 160 (issue_from.due_date || issue_from.start_date) + 1 + delay |
122 end | 161 end |
123 end | 162 end |
124 | 163 |
125 def <=>(relation) | 164 def <=>(relation) |
126 TYPES[self.relation_type][:order] <=> TYPES[relation.relation_type][:order] | 165 r = TYPES[self.relation_type][:order] <=> TYPES[relation.relation_type][:order] |
166 r == 0 ? id <=> relation.id : r | |
127 end | 167 end |
128 | 168 |
129 private | 169 private |
130 | 170 |
131 # Reverses the relation if needed so that it gets stored in the proper way | 171 # Reverses the relation if needed so that it gets stored in the proper way |