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