@@ -248,19 +248,22 class Issue < ActiveRecord::Base | |||||
248 | def safe_attributes=(attrs, user=User.current) |
|
248 | def safe_attributes=(attrs, user=User.current) | |
249 | return unless attrs.is_a?(Hash) |
|
249 | return unless attrs.is_a?(Hash) | |
250 |
|
250 | |||
251 | new_statuses_allowed = new_statuses_allowed_to(user) |
|
|||
252 |
|
||||
253 | # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed |
|
251 | # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed | |
254 | if new_record? || user.allowed_to?(:edit_issues, project) |
|
252 | if new_record? || user.allowed_to?(:edit_issues, project) | |
255 | attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES.include?(k)} |
|
253 | attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES.include?(k)} | |
256 | elsif new_statuses_allowed.any? |
|
254 | elsif new_statuses_allowed_to(user).any? | |
257 | attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES_ON_TRANSITION.include?(k)} |
|
255 | attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES_ON_TRANSITION.include?(k)} | |
258 | else |
|
256 | else | |
259 | return |
|
257 | return | |
260 | end |
|
258 | end | |
261 |
|
259 | |||
|
260 | # Tracker must be set before since new_statuses_allowed_to depends on it. | |||
|
261 | if t = attrs.delete('tracker_id') | |||
|
262 | self.tracker_id = t | |||
|
263 | end | |||
|
264 | ||||
262 | if attrs['status_id'] |
|
265 | if attrs['status_id'] | |
263 | unless new_statuses_allowed.collect(&:id).include?(attrs['status_id'].to_i) |
|
266 | unless new_statuses_allowed_to(user).collect(&:id).include?(attrs['status_id'].to_i) | |
264 | attrs.delete('status_id') |
|
267 | attrs.delete('status_id') | |
265 | end |
|
268 | end | |
266 | end |
|
269 | end |
@@ -116,36 +116,20 class MailHandler < ActionMailer::Base | |||||
116 | # Creates a new issue |
|
116 | # Creates a new issue | |
117 | def receive_issue |
|
117 | def receive_issue | |
118 | project = target_project |
|
118 | project = target_project | |
119 | tracker = (get_keyword(:tracker) && project.trackers.find_by_name(get_keyword(:tracker))) || project.trackers.find(:first) |
|
|||
120 | category = (get_keyword(:category) && project.issue_categories.find_by_name(get_keyword(:category))) |
|
|||
121 | priority = (get_keyword(:priority) && IssuePriority.find_by_name(get_keyword(:priority))) |
|
|||
122 | status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status))) |
|
|||
123 | assigned_to = (get_keyword(:assigned_to, :override => true) && find_user_from_keyword(get_keyword(:assigned_to, :override => true))) |
|
|||
124 | due_date = get_keyword(:due_date, :override => true) |
|
|||
125 | start_date = get_keyword(:start_date, :override => true) |
|
|||
126 |
|
||||
127 | # check permission |
|
119 | # check permission | |
128 | unless @@handler_options[:no_permission_check] |
|
120 | unless @@handler_options[:no_permission_check] | |
129 | raise UnauthorizedAction unless user.allowed_to?(:add_issues, project) |
|
121 | raise UnauthorizedAction unless user.allowed_to?(:add_issues, project) | |
130 | end |
|
122 | end | |
131 |
|
123 | |||
132 | issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority, :due_date => due_date, :start_date => start_date, :assigned_to => assigned_to) |
|
124 | issue = Issue.new(:author => user, :project => project) | |
133 | # check workflow |
|
125 | issue.safe_attributes = issue_attributes_from_keywords(issue) | |
134 | if status && issue.new_statuses_allowed_to(user).include?(status) |
|
126 | issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} | |
135 | issue.status = status |
|
127 | issue.subject = email.subject.to_s.chomp[0,255] | |
136 | end |
|
|||
137 | issue.subject = email.subject.chomp[0,255] |
|
|||
138 | if issue.subject.blank? |
|
128 | if issue.subject.blank? | |
139 | issue.subject = '(no subject)' |
|
129 | issue.subject = '(no subject)' | |
140 | end |
|
130 | end | |
141 | # custom fields |
|
|||
142 | issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c| |
|
|||
143 | if value = get_keyword(c.name, :override => true) |
|
|||
144 | h[c.id] = value |
|
|||
145 | end |
|
|||
146 | h |
|
|||
147 | end |
|
|||
148 | issue.description = cleaned_up_text_body |
|
131 | issue.description = cleaned_up_text_body | |
|
132 | ||||
149 | # add To and Cc as watchers before saving so the watchers can reply to Redmine |
|
133 | # add To and Cc as watchers before saving so the watchers can reply to Redmine | |
150 | add_watchers(issue) |
|
134 | add_watchers(issue) | |
151 | issue.save! |
|
135 | issue.save! | |
@@ -154,41 +138,19 class MailHandler < ActionMailer::Base | |||||
154 | issue |
|
138 | issue | |
155 | end |
|
139 | end | |
156 |
|
140 | |||
157 | def target_project |
|
|||
158 | # TODO: other ways to specify project: |
|
|||
159 | # * parse the email To field |
|
|||
160 | # * specific project (eg. Setting.mail_handler_target_project) |
|
|||
161 | target = Project.find_by_identifier(get_keyword(:project)) |
|
|||
162 | raise MissingInformation.new('Unable to determine target project') if target.nil? |
|
|||
163 | target |
|
|||
164 | end |
|
|||
165 |
|
||||
166 | # Adds a note to an existing issue |
|
141 | # Adds a note to an existing issue | |
167 | def receive_issue_reply(issue_id) |
|
142 | def receive_issue_reply(issue_id) | |
168 | status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status))) |
|
|||
169 | due_date = get_keyword(:due_date, :override => true) |
|
|||
170 | start_date = get_keyword(:start_date, :override => true) |
|
|||
171 | assigned_to = (get_keyword(:assigned_to, :override => true) && find_user_from_keyword(get_keyword(:assigned_to, :override => true))) |
|
|||
172 |
|
||||
173 | issue = Issue.find_by_id(issue_id) |
|
143 | issue = Issue.find_by_id(issue_id) | |
174 | return unless issue |
|
144 | return unless issue | |
175 | # check permission |
|
145 | # check permission | |
176 | unless @@handler_options[:no_permission_check] |
|
146 | unless @@handler_options[:no_permission_check] | |
177 | raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project) |
|
147 | raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project) | |
178 | raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project) |
|
|||
179 | end |
|
148 | end | |
180 |
|
149 | |||
181 | # add the note |
|
|||
182 | journal = issue.init_journal(user, cleaned_up_text_body) |
|
150 | journal = issue.init_journal(user, cleaned_up_text_body) | |
|
151 | issue.safe_attributes = issue_attributes_from_keywords(issue) | |||
|
152 | issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} | |||
183 | add_attachments(issue) |
|
153 | add_attachments(issue) | |
184 | # check workflow |
|
|||
185 | if status && issue.new_statuses_allowed_to(user).include?(status) |
|
|||
186 | issue.status = status |
|
|||
187 | end |
|
|||
188 | issue.start_date = start_date if start_date |
|
|||
189 | issue.due_date = due_date if due_date |
|
|||
190 | issue.assigned_to = assigned_to if assigned_to |
|
|||
191 |
|
||||
192 | issue.save! |
|
154 | issue.save! | |
193 | logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info |
|
155 | logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info | |
194 | journal |
|
156 | journal | |
@@ -263,6 +225,41 class MailHandler < ActionMailer::Base | |||||
263 | end |
|
225 | end | |
264 | end |
|
226 | end | |
265 | end |
|
227 | end | |
|
228 | ||||
|
229 | def target_project | |||
|
230 | # TODO: other ways to specify project: | |||
|
231 | # * parse the email To field | |||
|
232 | # * specific project (eg. Setting.mail_handler_target_project) | |||
|
233 | target = Project.find_by_identifier(get_keyword(:project)) | |||
|
234 | raise MissingInformation.new('Unable to determine target project') if target.nil? | |||
|
235 | target | |||
|
236 | end | |||
|
237 | ||||
|
238 | # Returns a Hash of issue attributes extracted from keywords in the email body | |||
|
239 | def issue_attributes_from_keywords(issue) | |||
|
240 | { | |||
|
241 | 'tracker_id' => ((k = get_keyword(:tracker)) && issue.project.trackers.find_by_name(k).try(:id)) || issue.project.trackers.find(:first).try(:id), | |||
|
242 | 'status_id' => (k = get_keyword(:status)) && IssueStatus.find_by_name(k).try(:id), | |||
|
243 | 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.find_by_name(k).try(:id), | |||
|
244 | 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.find_by_name(k).try(:id), | |||
|
245 | 'assigned_to_id' => (k = get_keyword(:assigned_to, :override => true)) && find_user_from_keyword(k).try(:id), | |||
|
246 | 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && issue.project.shared_versions.find_by_name(k).try(:id), | |||
|
247 | 'start_date' => get_keyword(:start_date, :override => true), | |||
|
248 | 'due_date' => get_keyword(:due_date, :override => true), | |||
|
249 | 'estimated_hours' => get_keyword(:estimated_hours, :override => true), | |||
|
250 | 'done_ratio' => get_keyword(:done_ratio, :override => true), | |||
|
251 | }.delete_if {|k, v| v.blank? } | |||
|
252 | end | |||
|
253 | ||||
|
254 | # Returns a Hash of issue custom field values extracted from keywords in the email body | |||
|
255 | def custom_field_values_from_keywords(customized) | |||
|
256 | customized.custom_field_values.inject({}) do |h, v| | |||
|
257 | if value = get_keyword(v.custom_field.name, :override => true) | |||
|
258 | h[v.custom_field.id.to_s] = value | |||
|
259 | end | |||
|
260 | h | |||
|
261 | end | |||
|
262 | end | |||
266 |
|
263 | |||
267 | # Returns the text/plain part of the email |
|
264 | # Returns the text/plain part of the email | |
268 | # If not found (eg. HTML-only email), returns the body with tags removed |
|
265 | # If not found (eg. HTML-only email), returns the body with tags removed |
@@ -54,4 +54,7 Status: Resolved | |||||
54 | due date: 2010-12-31 |
|
54 | due date: 2010-12-31 | |
55 | Start Date:2010-01-01 |
|
55 | Start Date:2010-01-01 | |
56 | Assigned to: John Smith |
|
56 | Assigned to: John Smith | |
|
57 | fixed version: alpha | |||
|
58 | estimated hours: 2.5 | |||
|
59 | done ratio: 30 | |||
57 |
|
60 |
@@ -29,6 +29,7 Status: Resolved | |||||
29 | due date: 2010-12-31 |
|
29 | due date: 2010-12-31 | |
30 | Start Date:2010-01-01 |
|
30 | Start Date:2010-01-01 | |
31 | Assigned to: jsmith@somenet.foo |
|
31 | Assigned to: jsmith@somenet.foo | |
|
32 | searchable field: Updated custom value | |||
32 |
|
33 | |||
33 | ------=_NextPart_000_0067_01C8D3CE.711F9CC0 |
|
34 | ------=_NextPart_000_0067_01C8D3CE.711F9CC0 | |
34 | Content-Type: text/html; |
|
35 | Content-Type: text/html; |
@@ -30,6 +30,7 class MailHandlerTest < ActiveSupport::TestCase | |||||
30 | :workflows, |
|
30 | :workflows, | |
31 | :trackers, |
|
31 | :trackers, | |
32 | :projects_trackers, |
|
32 | :projects_trackers, | |
|
33 | :versions, | |||
33 | :enumerations, |
|
34 | :enumerations, | |
34 | :issue_categories, |
|
35 | :issue_categories, | |
35 | :custom_fields, |
|
36 | :custom_fields, | |
@@ -59,6 +60,9 class MailHandlerTest < ActiveSupport::TestCase | |||||
59 | assert_equal '2010-01-01', issue.start_date.to_s |
|
60 | assert_equal '2010-01-01', issue.start_date.to_s | |
60 | assert_equal '2010-12-31', issue.due_date.to_s |
|
61 | assert_equal '2010-12-31', issue.due_date.to_s | |
61 | assert_equal User.find_by_login('jsmith'), issue.assigned_to |
|
62 | assert_equal User.find_by_login('jsmith'), issue.assigned_to | |
|
63 | assert_equal Version.find_by_name('alpha'), issue.fixed_version | |||
|
64 | assert_equal 2.5, issue.estimated_hours | |||
|
65 | assert_equal 30, issue.done_ratio | |||
62 | # keywords should be removed from the email body |
|
66 | # keywords should be removed from the email body | |
63 | assert !issue.description.match(/^Project:/i) |
|
67 | assert !issue.description.match(/^Project:/i) | |
64 | assert !issue.description.match(/^Status:/i) |
|
68 | assert !issue.description.match(/^Status:/i) | |
@@ -269,6 +273,7 class MailHandlerTest < ActiveSupport::TestCase | |||||
269 | assert_equal '2010-01-01', issue.start_date.to_s |
|
273 | assert_equal '2010-01-01', issue.start_date.to_s | |
270 | assert_equal '2010-12-31', issue.due_date.to_s |
|
274 | assert_equal '2010-12-31', issue.due_date.to_s | |
271 | assert_equal User.find_by_login('jsmith'), issue.assigned_to |
|
275 | assert_equal User.find_by_login('jsmith'), issue.assigned_to | |
|
276 | assert_equal 'Updated custom value', issue.custom_value_for(CustomField.find_by_name('Searchable field')).value | |||
272 | end |
|
277 | end | |
273 |
|
278 | |||
274 | def test_add_issue_note_should_send_email_notification |
|
279 | def test_add_issue_note_should_send_email_notification |
General Comments 0
You need to be logged in to leave comments.
Login now