##// END OF EJS Templates
Merged r13640 from 2.6-stable to trunk (#18280)...
Toshi MARUYAMA -
r13259:1ada789a6ccb
parent child
Show More
@@ -1,122 +1,123
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
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 IssueStatus < ActiveRecord::Base
18 class IssueStatus < ActiveRecord::Base
19 before_destroy :check_integrity
19 before_destroy :check_integrity
20 has_many :workflows, :class_name => 'WorkflowTransition', :foreign_key => "old_status_id"
20 has_many :workflows, :class_name => 'WorkflowTransition', :foreign_key => "old_status_id"
21 has_many :workflow_transitions_as_new_status, :class_name => 'WorkflowTransition', :foreign_key => "new_status_id"
21 has_many :workflow_transitions_as_new_status, :class_name => 'WorkflowTransition', :foreign_key => "new_status_id"
22 acts_as_list
22 acts_as_list
23
23
24 after_update :handle_is_closed_change
24 after_update :handle_is_closed_change
25 before_destroy :delete_workflow_rules
25 before_destroy :delete_workflow_rules
26
26
27 validates_presence_of :name
27 validates_presence_of :name
28 validates_uniqueness_of :name
28 validates_uniqueness_of :name
29 validates_length_of :name, :maximum => 30
29 validates_length_of :name, :maximum => 30
30 validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
30 validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
31 attr_protected :id
31 attr_protected :id
32
32
33 scope :sorted, lambda { order(:position) }
33 scope :sorted, lambda { order(:position) }
34 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
34 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
35
35
36 # Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+
36 # Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+
37 def self.update_issue_done_ratios
37 def self.update_issue_done_ratios
38 if Issue.use_status_for_done_ratio?
38 if Issue.use_status_for_done_ratio?
39 IssueStatus.where("default_done_ratio >= 0").each do |status|
39 IssueStatus.where("default_done_ratio >= 0").each do |status|
40 Issue.where({:status_id => status.id}).update_all({:done_ratio => status.default_done_ratio})
40 Issue.where({:status_id => status.id}).update_all({:done_ratio => status.default_done_ratio})
41 end
41 end
42 end
42 end
43
43
44 return Issue.use_status_for_done_ratio?
44 return Issue.use_status_for_done_ratio?
45 end
45 end
46
46
47 # Returns an array of all statuses the given role can switch to
47 # Returns an array of all statuses the given role can switch to
48 # Uses association cache when called more than one time
48 # Uses association cache when called more than one time
49 def new_statuses_allowed_to(roles, tracker, author=false, assignee=false)
49 def new_statuses_allowed_to(roles, tracker, author=false, assignee=false)
50 if roles && tracker
50 if roles && tracker
51 role_ids = roles.collect(&:id)
51 role_ids = roles.collect(&:id)
52 transitions = workflows.select do |w|
52 transitions = workflows.select do |w|
53 role_ids.include?(w.role_id) &&
53 role_ids.include?(w.role_id) &&
54 w.tracker_id == tracker.id &&
54 w.tracker_id == tracker.id &&
55 ((!w.author && !w.assignee) || (author && w.author) || (assignee && w.assignee))
55 ((!w.author && !w.assignee) || (author && w.author) || (assignee && w.assignee))
56 end
56 end
57 transitions.map(&:new_status).compact.sort
57 transitions.map(&:new_status).compact.sort
58 else
58 else
59 []
59 []
60 end
60 end
61 end
61 end
62
62
63 # Same thing as above but uses a database query
63 # Same thing as above but uses a database query
64 # More efficient than the previous method if called just once
64 # More efficient than the previous method if called just once
65 def find_new_statuses_allowed_to(roles, tracker, author=false, assignee=false)
65 def find_new_statuses_allowed_to(roles, tracker, author=false, assignee=false)
66 if roles.present? && tracker
66 if roles.present? && tracker
67 scope = IssueStatus.
67 scope = IssueStatus.
68 joins(:workflow_transitions_as_new_status).
68 joins(:workflow_transitions_as_new_status).
69 where(:workflows => {:old_status_id => id, :role_id => roles.map(&:id), :tracker_id => tracker.id})
69 where(:workflows => {:old_status_id => id, :role_id => roles.map(&:id), :tracker_id => tracker.id})
70
70
71 unless author && assignee
71 unless author && assignee
72 if author || assignee
72 if author || assignee
73 scope = scope.where("author = ? OR assignee = ?", author, assignee)
73 scope = scope.where("author = ? OR assignee = ?", author, assignee)
74 else
74 else
75 scope = scope.where("author = ? AND assignee = ?", false, false)
75 scope = scope.where("author = ? AND assignee = ?", false, false)
76 end
76 end
77 end
77 end
78
78
79 scope.uniq.to_a.sort
79 scope.uniq.to_a.sort
80 else
80 else
81 []
81 []
82 end
82 end
83 end
83 end
84
84
85 def <=>(status)
85 def <=>(status)
86 position <=> status.position
86 position <=> status.position
87 end
87 end
88
88
89 def to_s; name end
89 def to_s; name end
90
90
91 private
91 private
92
92
93 # Updates issues closed_on attribute when an existing status is set as closed.
93 # Updates issues closed_on attribute when an existing status is set as closed.
94 def handle_is_closed_change
94 def handle_is_closed_change
95 if is_closed_changed? && is_closed == true
95 if is_closed_changed? && is_closed == true
96 # First we update issues that have a journal for when the current status was set,
96 # First we update issues that have a journal for when the current status was set,
97 # a subselect is used to update all issues with a single query
97 # a subselect is used to update all issues with a single query
98 subselect = "SELECT MAX(j.created_on) FROM #{Journal.table_name} j" +
98 subselect = "SELECT MAX(j.created_on) FROM #{Journal.table_name} j" +
99 " JOIN #{JournalDetail.table_name} d ON d.journal_id = j.id" +
99 " JOIN #{JournalDetail.table_name} d ON d.journal_id = j.id" +
100 " WHERE j.journalized_type = 'Issue' AND j.journalized_id = #{Issue.table_name}.id" +
100 " WHERE j.journalized_type = 'Issue' AND j.journalized_id = #{Issue.table_name}.id" +
101 " AND d.property = 'attr' AND d.prop_key = 'status_id' AND d.value = :status_id"
101 " AND d.property = 'attr' AND d.prop_key = 'status_id' AND d.value = :status_id"
102 Issue.where(:status_id => id, :closed_on => nil).update_all(["closed_on = (#{subselect})", :status_id => id.to_s])
102 Issue.where(:status_id => id, :closed_on => nil).
103 update_all(["closed_on = (#{subselect})", {:status_id => id.to_s}])
103
104
104 # Then we update issues that don't have a journal which means the
105 # Then we update issues that don't have a journal which means the
105 # current status was set on creation
106 # current status was set on creation
106 Issue.where(:status_id => id, :closed_on => nil).update_all("closed_on = created_on")
107 Issue.where(:status_id => id, :closed_on => nil).update_all("closed_on = created_on")
107 end
108 end
108 end
109 end
109
110
110 def check_integrity
111 def check_integrity
111 if Issue.where(:status_id => id).any?
112 if Issue.where(:status_id => id).any?
112 raise "This status is used by some issues"
113 raise "This status is used by some issues"
113 elsif Tracker.where(:default_status_id => id).any?
114 elsif Tracker.where(:default_status_id => id).any?
114 raise "This status is used as the default status by some trackers"
115 raise "This status is used as the default status by some trackers"
115 end
116 end
116 end
117 end
117
118
118 # Deletes associated workflows
119 # Deletes associated workflows
119 def delete_workflow_rules
120 def delete_workflow_rules
120 WorkflowRule.delete_all(["old_status_id = :id OR new_status_id = :id", {:id => id}])
121 WorkflowRule.delete_all(["old_status_id = :id OR new_status_id = :id", {:id => id}])
121 end
122 end
122 end
123 end
General Comments 0
You need to be logged in to leave comments. Login now