##// END OF EJS Templates
Makes MailHandler accept all issue attributes and custom fields that can be set/updated (#4071, #4807, #5622, #6110)....
Jean-Philippe Lang -
r4280:e0e8c14c2aef
parent child
Show More
@@ -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