Mercurial > hg > soundsoftware-site
comparison app/models/mail_handler.rb @ 37:94944d00e43c
* Update to SVN trunk rev 4411
author | Chris Cannam <chris.cannam@soundsoftware.ac.uk> |
---|---|
date | Fri, 19 Nov 2010 13:24:41 +0000 |
parents | 513646585e45 |
children | af80e5618e9b 8661b858af72 |
comparison
equal
deleted
inserted
replaced
22:40f7cfd4df19 | 37:94944d00e43c |
---|---|
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 MailHandler < ActionMailer::Base | 18 class MailHandler < ActionMailer::Base |
19 include ActionView::Helpers::SanitizeHelper | 19 include ActionView::Helpers::SanitizeHelper |
20 include Redmine::I18n | |
20 | 21 |
21 class UnauthorizedAction < StandardError; end | 22 class UnauthorizedAction < StandardError; end |
22 class MissingInformation < StandardError; end | 23 class MissingInformation < StandardError; end |
23 | 24 |
24 attr_reader :email, :user | 25 attr_reader :email, :user |
114 end | 115 end |
115 | 116 |
116 # Creates a new issue | 117 # Creates a new issue |
117 def receive_issue | 118 def receive_issue |
118 project = target_project | 119 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 | 120 # check permission |
128 unless @@handler_options[:no_permission_check] | 121 unless @@handler_options[:no_permission_check] |
129 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project) | 122 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project) |
130 end | 123 end |
131 | 124 |
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) | 125 issue = Issue.new(:author => user, :project => project) |
133 # check workflow | 126 issue.safe_attributes = issue_attributes_from_keywords(issue) |
134 if status && issue.new_statuses_allowed_to(user).include?(status) | 127 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} |
135 issue.status = status | 128 issue.subject = email.subject.to_s.chomp[0,255] |
136 end | |
137 issue.subject = email.subject.chomp[0,255] | |
138 if issue.subject.blank? | 129 if issue.subject.blank? |
139 issue.subject = '(no subject)' | 130 issue.subject = '(no subject)' |
140 end | 131 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 | 132 issue.description = cleaned_up_text_body |
133 | |
149 # add To and Cc as watchers before saving so the watchers can reply to Redmine | 134 # add To and Cc as watchers before saving so the watchers can reply to Redmine |
150 add_watchers(issue) | 135 add_watchers(issue) |
151 issue.save! | 136 issue.save! |
152 add_attachments(issue) | 137 add_attachments(issue) |
153 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info | 138 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info |
154 issue | 139 issue |
155 end | 140 end |
156 | 141 |
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 | 142 # Adds a note to an existing issue |
167 def receive_issue_reply(issue_id) | 143 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) | 144 issue = Issue.find_by_id(issue_id) |
174 return unless issue | 145 return unless issue |
175 # check permission | 146 # check permission |
176 unless @@handler_options[:no_permission_check] | 147 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) | 148 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) | 149 end |
179 end | 150 |
180 | |
181 # add the note | |
182 journal = issue.init_journal(user, cleaned_up_text_body) | 151 journal = issue.init_journal(user, cleaned_up_text_body) |
152 issue.safe_attributes = issue_attributes_from_keywords(issue) | |
153 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} | |
183 add_attachments(issue) | 154 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! | 155 issue.save! |
193 logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info | 156 logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info |
194 journal | 157 journal |
195 end | 158 end |
196 | 159 |
253 @keywords ||= {} | 216 @keywords ||= {} |
254 if @keywords.has_key?(attr) | 217 if @keywords.has_key?(attr) |
255 @keywords[attr] | 218 @keywords[attr] |
256 else | 219 else |
257 @keywords[attr] = begin | 220 @keywords[attr] = begin |
258 if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr.to_s.humanize}[ \t]*:[ \t]*(.+)\s*$/i, '') | 221 if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && (v = extract_keyword!(plain_text_body, attr, options[:format])) |
259 $1.strip | 222 v |
260 elsif !@@handler_options[:issue][attr].blank? | 223 elsif !@@handler_options[:issue][attr].blank? |
261 @@handler_options[:issue][attr] | 224 @@handler_options[:issue][attr] |
262 end | 225 end |
263 end | 226 end |
227 end | |
228 end | |
229 | |
230 # Destructively extracts the value for +attr+ in +text+ | |
231 # Returns nil if no matching keyword found | |
232 def extract_keyword!(text, attr, format=nil) | |
233 keys = [attr.to_s.humanize] | |
234 if attr.is_a?(Symbol) | |
235 keys << l("field_#{attr}", :default => '', :locale => user.language) if user | |
236 keys << l("field_#{attr}", :default => '', :locale => Setting.default_language) | |
237 end | |
238 keys.reject! {|k| k.blank?} | |
239 keys.collect! {|k| Regexp.escape(k)} | |
240 format ||= '.+' | |
241 text.gsub!(/^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i, '') | |
242 $2 && $2.strip | |
243 end | |
244 | |
245 def target_project | |
246 # TODO: other ways to specify project: | |
247 # * parse the email To field | |
248 # * specific project (eg. Setting.mail_handler_target_project) | |
249 target = Project.find_by_identifier(get_keyword(:project)) | |
250 raise MissingInformation.new('Unable to determine target project') if target.nil? | |
251 target | |
252 end | |
253 | |
254 # Returns a Hash of issue attributes extracted from keywords in the email body | |
255 def issue_attributes_from_keywords(issue) | |
256 assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_user_from_keyword(k) | |
257 assigned_to = nil if assigned_to && !issue.assignable_users.include?(assigned_to) | |
258 | |
259 { | |
260 'tracker_id' => ((k = get_keyword(:tracker)) && issue.project.trackers.find_by_name(k).try(:id)) || issue.project.trackers.find(:first).try(:id), | |
261 'status_id' => (k = get_keyword(:status)) && IssueStatus.find_by_name(k).try(:id), | |
262 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.find_by_name(k).try(:id), | |
263 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.find_by_name(k).try(:id), | |
264 'assigned_to_id' => assigned_to.try(:id), | |
265 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && issue.project.shared_versions.find_by_name(k).try(:id), | |
266 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), | |
267 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), | |
268 'estimated_hours' => get_keyword(:estimated_hours, :override => true), | |
269 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0') | |
270 }.delete_if {|k, v| v.blank? } | |
271 end | |
272 | |
273 # Returns a Hash of issue custom field values extracted from keywords in the email body | |
274 def custom_field_values_from_keywords(customized) | |
275 customized.custom_field_values.inject({}) do |h, v| | |
276 if value = get_keyword(v.custom_field.name, :override => true) | |
277 h[v.custom_field.id.to_s] = value | |
278 end | |
279 h | |
264 end | 280 end |
265 end | 281 end |
266 | 282 |
267 # Returns the text/plain part of the email | 283 # Returns the text/plain part of the email |
268 # If not found (eg. HTML-only email), returns the body with tags removed | 284 # If not found (eg. HTML-only email), returns the body with tags removed |
316 | 332 |
317 # Removes the email body of text after the truncation configurations. | 333 # Removes the email body of text after the truncation configurations. |
318 def cleanup_body(body) | 334 def cleanup_body(body) |
319 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)} | 335 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)} |
320 unless delimiters.empty? | 336 unless delimiters.empty? |
321 regex = Regexp.new("^(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE) | 337 regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE) |
322 body = body.gsub(regex, '') | 338 body = body.gsub(regex, '') |
323 end | 339 end |
324 body.strip | 340 body.strip |
325 end | 341 end |
326 | 342 |