##// END OF EJS Templates
Merged r3783 from trunk....
Eric Davis -
r3677:e6d053c3fd5f
parent child
Show More
@@ -0,0 +1,17
1 Return-Path: <john.doe@somenet.foo>
2 Received: from osiris ([127.0.0.1])
3 by OSIRIS
4 with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
5 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
6 To: <redmine@somenet.foo>
7 Subject: Ticket by unknown user
8 Date: Sun, 22 Jun 2008 12:28:07 +0200
9 MIME-Version: 1.0
10 Content-Type: text/plain;
11 format=flowed;
12 charset="iso-8859-1";
13 reply-type=original
14 Content-Transfer-Encoding: 7bit
15
16 This is a ticket submitted by an unknown user.
17
@@ -1,316 +1,316
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 MailHandler < ActionMailer::Base
18 class MailHandler < ActionMailer::Base
19 include ActionView::Helpers::SanitizeHelper
19 include ActionView::Helpers::SanitizeHelper
20
20
21 class UnauthorizedAction < StandardError; end
21 class UnauthorizedAction < StandardError; end
22 class MissingInformation < StandardError; end
22 class MissingInformation < StandardError; end
23
23
24 attr_reader :email, :user
24 attr_reader :email, :user
25
25
26 def self.receive(email, options={})
26 def self.receive(email, options={})
27 @@handler_options = options.dup
27 @@handler_options = options.dup
28
28
29 @@handler_options[:issue] ||= {}
29 @@handler_options[:issue] ||= {}
30
30
31 @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) if @@handler_options[:allow_override].is_a?(String)
31 @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) if @@handler_options[:allow_override].is_a?(String)
32 @@handler_options[:allow_override] ||= []
32 @@handler_options[:allow_override] ||= []
33 # Project needs to be overridable if not specified
33 # Project needs to be overridable if not specified
34 @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
34 @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
35 # Status overridable by default
35 # Status overridable by default
36 @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
36 @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
37
37
38 @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1' ? true : false)
38 @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1' ? true : false)
39 super email
39 super email
40 end
40 end
41
41
42 # Processes incoming emails
42 # Processes incoming emails
43 # Returns the created object (eg. an issue, a message) or false
43 # Returns the created object (eg. an issue, a message) or false
44 def receive(email)
44 def receive(email)
45 @email = email
45 @email = email
46 sender_email = email.from.to_a.first.to_s.strip
46 sender_email = email.from.to_a.first.to_s.strip
47 # Ignore emails received from the application emission address to avoid hell cycles
47 # Ignore emails received from the application emission address to avoid hell cycles
48 if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
48 if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
49 logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]" if logger && logger.info
49 logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]" if logger && logger.info
50 return false
50 return false
51 end
51 end
52 @user = User.find_by_mail(sender_email)
52 @user = User.find_by_mail(sender_email) if sender_email.present?
53 if @user && !@user.active?
53 if @user && !@user.active?
54 logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]" if logger && logger.info
54 logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]" if logger && logger.info
55 return false
55 return false
56 end
56 end
57 if @user.nil?
57 if @user.nil?
58 # Email was submitted by an unknown user
58 # Email was submitted by an unknown user
59 case @@handler_options[:unknown_user]
59 case @@handler_options[:unknown_user]
60 when 'accept'
60 when 'accept'
61 @user = User.anonymous
61 @user = User.anonymous
62 when 'create'
62 when 'create'
63 @user = MailHandler.create_user_from_email(email)
63 @user = MailHandler.create_user_from_email(email)
64 if @user
64 if @user
65 logger.info "MailHandler: [#{@user.login}] account created" if logger && logger.info
65 logger.info "MailHandler: [#{@user.login}] account created" if logger && logger.info
66 Mailer.deliver_account_information(@user, @user.password)
66 Mailer.deliver_account_information(@user, @user.password)
67 else
67 else
68 logger.error "MailHandler: could not create account for [#{sender_email}]" if logger && logger.error
68 logger.error "MailHandler: could not create account for [#{sender_email}]" if logger && logger.error
69 return false
69 return false
70 end
70 end
71 else
71 else
72 # Default behaviour, emails from unknown users are ignored
72 # Default behaviour, emails from unknown users are ignored
73 logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]" if logger && logger.info
73 logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]" if logger && logger.info
74 return false
74 return false
75 end
75 end
76 end
76 end
77 User.current = @user
77 User.current = @user
78 dispatch
78 dispatch
79 end
79 end
80
80
81 private
81 private
82
82
83 MESSAGE_ID_RE = %r{^<redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
83 MESSAGE_ID_RE = %r{^<redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
84 ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]}
84 ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]}
85 MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
85 MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
86
86
87 def dispatch
87 def dispatch
88 headers = [email.in_reply_to, email.references].flatten.compact
88 headers = [email.in_reply_to, email.references].flatten.compact
89 if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
89 if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
90 klass, object_id = $1, $2.to_i
90 klass, object_id = $1, $2.to_i
91 method_name = "receive_#{klass}_reply"
91 method_name = "receive_#{klass}_reply"
92 if self.class.private_instance_methods.collect(&:to_s).include?(method_name)
92 if self.class.private_instance_methods.collect(&:to_s).include?(method_name)
93 send method_name, object_id
93 send method_name, object_id
94 else
94 else
95 # ignoring it
95 # ignoring it
96 end
96 end
97 elsif m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
97 elsif m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
98 receive_issue_reply(m[1].to_i)
98 receive_issue_reply(m[1].to_i)
99 elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE)
99 elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE)
100 receive_message_reply(m[1].to_i)
100 receive_message_reply(m[1].to_i)
101 else
101 else
102 receive_issue
102 receive_issue
103 end
103 end
104 rescue ActiveRecord::RecordInvalid => e
104 rescue ActiveRecord::RecordInvalid => e
105 # TODO: send a email to the user
105 # TODO: send a email to the user
106 logger.error e.message if logger
106 logger.error e.message if logger
107 false
107 false
108 rescue MissingInformation => e
108 rescue MissingInformation => e
109 logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
109 logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
110 false
110 false
111 rescue UnauthorizedAction => e
111 rescue UnauthorizedAction => e
112 logger.error "MailHandler: unauthorized attempt from #{user}" if logger
112 logger.error "MailHandler: unauthorized attempt from #{user}" if logger
113 false
113 false
114 end
114 end
115
115
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)
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)))
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)))
121 priority = (get_keyword(:priority) && IssuePriority.find_by_name(get_keyword(:priority)))
122 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
122 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
123
123
124 # check permission
124 # check permission
125 unless @@handler_options[:no_permission_check]
125 unless @@handler_options[:no_permission_check]
126 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
126 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
127 end
127 end
128
128
129 issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority)
129 issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority)
130 # check workflow
130 # check workflow
131 if status && issue.new_statuses_allowed_to(user).include?(status)
131 if status && issue.new_statuses_allowed_to(user).include?(status)
132 issue.status = status
132 issue.status = status
133 end
133 end
134 issue.subject = email.subject.chomp
134 issue.subject = email.subject.chomp
135 if issue.subject.blank?
135 if issue.subject.blank?
136 issue.subject = '(no subject)'
136 issue.subject = '(no subject)'
137 end
137 end
138 # custom fields
138 # custom fields
139 issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
139 issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
140 if value = get_keyword(c.name, :override => true)
140 if value = get_keyword(c.name, :override => true)
141 h[c.id] = value
141 h[c.id] = value
142 end
142 end
143 h
143 h
144 end
144 end
145 issue.description = cleaned_up_text_body
145 issue.description = cleaned_up_text_body
146 # add To and Cc as watchers before saving so the watchers can reply to Redmine
146 # add To and Cc as watchers before saving so the watchers can reply to Redmine
147 add_watchers(issue)
147 add_watchers(issue)
148 issue.save!
148 issue.save!
149 add_attachments(issue)
149 add_attachments(issue)
150 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
150 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
151 issue
151 issue
152 end
152 end
153
153
154 def target_project
154 def target_project
155 # TODO: other ways to specify project:
155 # TODO: other ways to specify project:
156 # * parse the email To field
156 # * parse the email To field
157 # * specific project (eg. Setting.mail_handler_target_project)
157 # * specific project (eg. Setting.mail_handler_target_project)
158 target = Project.find_by_identifier(get_keyword(:project))
158 target = Project.find_by_identifier(get_keyword(:project))
159 raise MissingInformation.new('Unable to determine target project') if target.nil?
159 raise MissingInformation.new('Unable to determine target project') if target.nil?
160 target
160 target
161 end
161 end
162
162
163 # Adds a note to an existing issue
163 # Adds a note to an existing issue
164 def receive_issue_reply(issue_id)
164 def receive_issue_reply(issue_id)
165 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
165 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
166
166
167 issue = Issue.find_by_id(issue_id)
167 issue = Issue.find_by_id(issue_id)
168 return unless issue
168 return unless issue
169 # check permission
169 # check permission
170 unless @@handler_options[:no_permission_check]
170 unless @@handler_options[:no_permission_check]
171 raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
171 raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
172 raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project)
172 raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project)
173 end
173 end
174
174
175 # add the note
175 # add the note
176 journal = issue.init_journal(user, cleaned_up_text_body)
176 journal = issue.init_journal(user, cleaned_up_text_body)
177 add_attachments(issue)
177 add_attachments(issue)
178 # check workflow
178 # check workflow
179 if status && issue.new_statuses_allowed_to(user).include?(status)
179 if status && issue.new_statuses_allowed_to(user).include?(status)
180 issue.status = status
180 issue.status = status
181 end
181 end
182 issue.save!
182 issue.save!
183 logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
183 logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
184 journal
184 journal
185 end
185 end
186
186
187 # Reply will be added to the issue
187 # Reply will be added to the issue
188 def receive_journal_reply(journal_id)
188 def receive_journal_reply(journal_id)
189 journal = Journal.find_by_id(journal_id)
189 journal = Journal.find_by_id(journal_id)
190 if journal && journal.journalized_type == 'Issue'
190 if journal && journal.journalized_type == 'Issue'
191 receive_issue_reply(journal.journalized_id)
191 receive_issue_reply(journal.journalized_id)
192 end
192 end
193 end
193 end
194
194
195 # Receives a reply to a forum message
195 # Receives a reply to a forum message
196 def receive_message_reply(message_id)
196 def receive_message_reply(message_id)
197 message = Message.find_by_id(message_id)
197 message = Message.find_by_id(message_id)
198 if message
198 if message
199 message = message.root
199 message = message.root
200
200
201 unless @@handler_options[:no_permission_check]
201 unless @@handler_options[:no_permission_check]
202 raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project)
202 raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project)
203 end
203 end
204
204
205 if !message.locked?
205 if !message.locked?
206 reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
206 reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
207 :content => cleaned_up_text_body)
207 :content => cleaned_up_text_body)
208 reply.author = user
208 reply.author = user
209 reply.board = message.board
209 reply.board = message.board
210 message.children << reply
210 message.children << reply
211 add_attachments(reply)
211 add_attachments(reply)
212 reply
212 reply
213 else
213 else
214 logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" if logger && logger.info
214 logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" if logger && logger.info
215 end
215 end
216 end
216 end
217 end
217 end
218
218
219 def add_attachments(obj)
219 def add_attachments(obj)
220 if email.has_attachments?
220 if email.has_attachments?
221 email.attachments.each do |attachment|
221 email.attachments.each do |attachment|
222 Attachment.create(:container => obj,
222 Attachment.create(:container => obj,
223 :file => attachment,
223 :file => attachment,
224 :author => user,
224 :author => user,
225 :content_type => attachment.content_type)
225 :content_type => attachment.content_type)
226 end
226 end
227 end
227 end
228 end
228 end
229
229
230 # Adds To and Cc as watchers of the given object if the sender has the
230 # Adds To and Cc as watchers of the given object if the sender has the
231 # appropriate permission
231 # appropriate permission
232 def add_watchers(obj)
232 def add_watchers(obj)
233 if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
233 if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
234 addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
234 addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
235 unless addresses.empty?
235 unless addresses.empty?
236 watchers = User.active.find(:all, :conditions => ['LOWER(mail) IN (?)', addresses])
236 watchers = User.active.find(:all, :conditions => ['LOWER(mail) IN (?)', addresses])
237 watchers.each {|w| obj.add_watcher(w)}
237 watchers.each {|w| obj.add_watcher(w)}
238 end
238 end
239 end
239 end
240 end
240 end
241
241
242 def get_keyword(attr, options={})
242 def get_keyword(attr, options={})
243 @keywords ||= {}
243 @keywords ||= {}
244 if @keywords.has_key?(attr)
244 if @keywords.has_key?(attr)
245 @keywords[attr]
245 @keywords[attr]
246 else
246 else
247 @keywords[attr] = begin
247 @keywords[attr] = begin
248 if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr}[ \t]*:[ \t]*(.+)\s*$/i, '')
248 if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr}[ \t]*:[ \t]*(.+)\s*$/i, '')
249 $1.strip
249 $1.strip
250 elsif !@@handler_options[:issue][attr].blank?
250 elsif !@@handler_options[:issue][attr].blank?
251 @@handler_options[:issue][attr]
251 @@handler_options[:issue][attr]
252 end
252 end
253 end
253 end
254 end
254 end
255 end
255 end
256
256
257 # Returns the text/plain part of the email
257 # Returns the text/plain part of the email
258 # If not found (eg. HTML-only email), returns the body with tags removed
258 # If not found (eg. HTML-only email), returns the body with tags removed
259 def plain_text_body
259 def plain_text_body
260 return @plain_text_body unless @plain_text_body.nil?
260 return @plain_text_body unless @plain_text_body.nil?
261 parts = @email.parts.collect {|c| (c.respond_to?(:parts) && !c.parts.empty?) ? c.parts : c}.flatten
261 parts = @email.parts.collect {|c| (c.respond_to?(:parts) && !c.parts.empty?) ? c.parts : c}.flatten
262 if parts.empty?
262 if parts.empty?
263 parts << @email
263 parts << @email
264 end
264 end
265 plain_text_part = parts.detect {|p| p.content_type == 'text/plain'}
265 plain_text_part = parts.detect {|p| p.content_type == 'text/plain'}
266 if plain_text_part.nil?
266 if plain_text_part.nil?
267 # no text/plain part found, assuming html-only email
267 # no text/plain part found, assuming html-only email
268 # strip html tags and remove doctype directive
268 # strip html tags and remove doctype directive
269 @plain_text_body = strip_tags(@email.body.to_s)
269 @plain_text_body = strip_tags(@email.body.to_s)
270 @plain_text_body.gsub! %r{^<!DOCTYPE .*$}, ''
270 @plain_text_body.gsub! %r{^<!DOCTYPE .*$}, ''
271 else
271 else
272 @plain_text_body = plain_text_part.body.to_s
272 @plain_text_body = plain_text_part.body.to_s
273 end
273 end
274 @plain_text_body.strip!
274 @plain_text_body.strip!
275 @plain_text_body
275 @plain_text_body
276 end
276 end
277
277
278 def cleaned_up_text_body
278 def cleaned_up_text_body
279 cleanup_body(plain_text_body)
279 cleanup_body(plain_text_body)
280 end
280 end
281
281
282 def self.full_sanitizer
282 def self.full_sanitizer
283 @full_sanitizer ||= HTML::FullSanitizer.new
283 @full_sanitizer ||= HTML::FullSanitizer.new
284 end
284 end
285
285
286 # Creates a user account for the +email+ sender
286 # Creates a user account for the +email+ sender
287 def self.create_user_from_email(email)
287 def self.create_user_from_email(email)
288 addr = email.from_addrs.to_a.first
288 addr = email.from_addrs.to_a.first
289 if addr && !addr.spec.blank?
289 if addr && !addr.spec.blank?
290 user = User.new
290 user = User.new
291 user.mail = addr.spec
291 user.mail = addr.spec
292
292
293 names = addr.name.blank? ? addr.spec.gsub(/@.*$/, '').split('.') : addr.name.split
293 names = addr.name.blank? ? addr.spec.gsub(/@.*$/, '').split('.') : addr.name.split
294 user.firstname = names.shift
294 user.firstname = names.shift
295 user.lastname = names.join(' ')
295 user.lastname = names.join(' ')
296 user.lastname = '-' if user.lastname.blank?
296 user.lastname = '-' if user.lastname.blank?
297
297
298 user.login = user.mail
298 user.login = user.mail
299 user.password = ActiveSupport::SecureRandom.hex(5)
299 user.password = ActiveSupport::SecureRandom.hex(5)
300 user.language = Setting.default_language
300 user.language = Setting.default_language
301 user.save ? user : nil
301 user.save ? user : nil
302 end
302 end
303 end
303 end
304
304
305 private
305 private
306
306
307 # Removes the email body of text after the truncation configurations.
307 # Removes the email body of text after the truncation configurations.
308 def cleanup_body(body)
308 def cleanup_body(body)
309 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
309 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
310 unless delimiters.empty?
310 unless delimiters.empty?
311 regex = Regexp.new("^(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
311 regex = Regexp.new("^(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
312 body = body.gsub(regex, '')
312 body = body.gsub(regex, '')
313 end
313 end
314 body.strip
314 body.strip
315 end
315 end
316 end
316 end
@@ -1,342 +1,351
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2009 Jean-Philippe Lang
4 # Copyright (C) 2006-2009 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.dirname(__FILE__) + '/../test_helper'
20 require File.dirname(__FILE__) + '/../test_helper'
21
21
22 class MailHandlerTest < ActiveSupport::TestCase
22 class MailHandlerTest < ActiveSupport::TestCase
23 fixtures :users, :projects,
23 fixtures :users, :projects,
24 :enabled_modules,
24 :enabled_modules,
25 :roles,
25 :roles,
26 :members,
26 :members,
27 :member_roles,
27 :member_roles,
28 :issues,
28 :issues,
29 :issue_statuses,
29 :issue_statuses,
30 :workflows,
30 :workflows,
31 :trackers,
31 :trackers,
32 :projects_trackers,
32 :projects_trackers,
33 :enumerations,
33 :enumerations,
34 :issue_categories,
34 :issue_categories,
35 :custom_fields,
35 :custom_fields,
36 :custom_fields_trackers,
36 :custom_fields_trackers,
37 :boards,
37 :boards,
38 :messages
38 :messages
39
39
40 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
40 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
41
41
42 def setup
42 def setup
43 ActionMailer::Base.deliveries.clear
43 ActionMailer::Base.deliveries.clear
44 end
44 end
45
45
46 def test_add_issue
46 def test_add_issue
47 ActionMailer::Base.deliveries.clear
47 ActionMailer::Base.deliveries.clear
48 # This email contains: 'Project: onlinestore'
48 # This email contains: 'Project: onlinestore'
49 issue = submit_email('ticket_on_given_project.eml')
49 issue = submit_email('ticket_on_given_project.eml')
50 assert issue.is_a?(Issue)
50 assert issue.is_a?(Issue)
51 assert !issue.new_record?
51 assert !issue.new_record?
52 issue.reload
52 issue.reload
53 assert_equal 'New ticket on a given project', issue.subject
53 assert_equal 'New ticket on a given project', issue.subject
54 assert_equal User.find_by_login('jsmith'), issue.author
54 assert_equal User.find_by_login('jsmith'), issue.author
55 assert_equal Project.find(2), issue.project
55 assert_equal Project.find(2), issue.project
56 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
56 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
57 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
57 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
58 # keywords should be removed from the email body
58 # keywords should be removed from the email body
59 assert !issue.description.match(/^Project:/i)
59 assert !issue.description.match(/^Project:/i)
60 assert !issue.description.match(/^Status:/i)
60 assert !issue.description.match(/^Status:/i)
61 # Email notification should be sent
61 # Email notification should be sent
62 mail = ActionMailer::Base.deliveries.last
62 mail = ActionMailer::Base.deliveries.last
63 assert_not_nil mail
63 assert_not_nil mail
64 assert mail.subject.include?('New ticket on a given project')
64 assert mail.subject.include?('New ticket on a given project')
65 end
65 end
66
66
67 def test_add_issue_with_status
67 def test_add_issue_with_status
68 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
68 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
69 issue = submit_email('ticket_on_given_project.eml')
69 issue = submit_email('ticket_on_given_project.eml')
70 assert issue.is_a?(Issue)
70 assert issue.is_a?(Issue)
71 assert !issue.new_record?
71 assert !issue.new_record?
72 issue.reload
72 issue.reload
73 assert_equal Project.find(2), issue.project
73 assert_equal Project.find(2), issue.project
74 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
74 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
75 end
75 end
76
76
77 def test_add_issue_with_attributes_override
77 def test_add_issue_with_attributes_override
78 issue = submit_email('ticket_with_attributes.eml', :allow_override => 'tracker,category,priority')
78 issue = submit_email('ticket_with_attributes.eml', :allow_override => 'tracker,category,priority')
79 assert issue.is_a?(Issue)
79 assert issue.is_a?(Issue)
80 assert !issue.new_record?
80 assert !issue.new_record?
81 issue.reload
81 issue.reload
82 assert_equal 'New ticket on a given project', issue.subject
82 assert_equal 'New ticket on a given project', issue.subject
83 assert_equal User.find_by_login('jsmith'), issue.author
83 assert_equal User.find_by_login('jsmith'), issue.author
84 assert_equal Project.find(2), issue.project
84 assert_equal Project.find(2), issue.project
85 assert_equal 'Feature request', issue.tracker.to_s
85 assert_equal 'Feature request', issue.tracker.to_s
86 assert_equal 'Stock management', issue.category.to_s
86 assert_equal 'Stock management', issue.category.to_s
87 assert_equal 'Urgent', issue.priority.to_s
87 assert_equal 'Urgent', issue.priority.to_s
88 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
88 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
89 end
89 end
90
90
91 def test_add_issue_with_partial_attributes_override
91 def test_add_issue_with_partial_attributes_override
92 issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker'])
92 issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker'])
93 assert issue.is_a?(Issue)
93 assert issue.is_a?(Issue)
94 assert !issue.new_record?
94 assert !issue.new_record?
95 issue.reload
95 issue.reload
96 assert_equal 'New ticket on a given project', issue.subject
96 assert_equal 'New ticket on a given project', issue.subject
97 assert_equal User.find_by_login('jsmith'), issue.author
97 assert_equal User.find_by_login('jsmith'), issue.author
98 assert_equal Project.find(2), issue.project
98 assert_equal Project.find(2), issue.project
99 assert_equal 'Feature request', issue.tracker.to_s
99 assert_equal 'Feature request', issue.tracker.to_s
100 assert_nil issue.category
100 assert_nil issue.category
101 assert_equal 'High', issue.priority.to_s
101 assert_equal 'High', issue.priority.to_s
102 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
102 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
103 end
103 end
104
104
105 def test_add_issue_with_spaces_between_attribute_and_separator
105 def test_add_issue_with_spaces_between_attribute_and_separator
106 issue = submit_email('ticket_with_spaces_between_attribute_and_separator.eml', :allow_override => 'tracker,category,priority')
106 issue = submit_email('ticket_with_spaces_between_attribute_and_separator.eml', :allow_override => 'tracker,category,priority')
107 assert issue.is_a?(Issue)
107 assert issue.is_a?(Issue)
108 assert !issue.new_record?
108 assert !issue.new_record?
109 issue.reload
109 issue.reload
110 assert_equal 'New ticket on a given project', issue.subject
110 assert_equal 'New ticket on a given project', issue.subject
111 assert_equal User.find_by_login('jsmith'), issue.author
111 assert_equal User.find_by_login('jsmith'), issue.author
112 assert_equal Project.find(2), issue.project
112 assert_equal Project.find(2), issue.project
113 assert_equal 'Feature request', issue.tracker.to_s
113 assert_equal 'Feature request', issue.tracker.to_s
114 assert_equal 'Stock management', issue.category.to_s
114 assert_equal 'Stock management', issue.category.to_s
115 assert_equal 'Urgent', issue.priority.to_s
115 assert_equal 'Urgent', issue.priority.to_s
116 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
116 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
117 end
117 end
118
118
119
119
120 def test_add_issue_with_attachment_to_specific_project
120 def test_add_issue_with_attachment_to_specific_project
121 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
121 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
122 assert issue.is_a?(Issue)
122 assert issue.is_a?(Issue)
123 assert !issue.new_record?
123 assert !issue.new_record?
124 issue.reload
124 issue.reload
125 assert_equal 'Ticket created by email with attachment', issue.subject
125 assert_equal 'Ticket created by email with attachment', issue.subject
126 assert_equal User.find_by_login('jsmith'), issue.author
126 assert_equal User.find_by_login('jsmith'), issue.author
127 assert_equal Project.find(2), issue.project
127 assert_equal Project.find(2), issue.project
128 assert_equal 'This is a new ticket with attachments', issue.description
128 assert_equal 'This is a new ticket with attachments', issue.description
129 # Attachment properties
129 # Attachment properties
130 assert_equal 1, issue.attachments.size
130 assert_equal 1, issue.attachments.size
131 assert_equal 'Paella.jpg', issue.attachments.first.filename
131 assert_equal 'Paella.jpg', issue.attachments.first.filename
132 assert_equal 'image/jpeg', issue.attachments.first.content_type
132 assert_equal 'image/jpeg', issue.attachments.first.content_type
133 assert_equal 10790, issue.attachments.first.filesize
133 assert_equal 10790, issue.attachments.first.filesize
134 end
134 end
135
135
136 def test_add_issue_with_custom_fields
136 def test_add_issue_with_custom_fields
137 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
137 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
138 assert issue.is_a?(Issue)
138 assert issue.is_a?(Issue)
139 assert !issue.new_record?
139 assert !issue.new_record?
140 issue.reload
140 issue.reload
141 assert_equal 'New ticket with custom field values', issue.subject
141 assert_equal 'New ticket with custom field values', issue.subject
142 assert_equal 'Value for a custom field', issue.custom_value_for(CustomField.find_by_name('Searchable field')).value
142 assert_equal 'Value for a custom field', issue.custom_value_for(CustomField.find_by_name('Searchable field')).value
143 assert !issue.description.match(/^searchable field:/i)
143 assert !issue.description.match(/^searchable field:/i)
144 end
144 end
145
145
146 def test_add_issue_with_cc
146 def test_add_issue_with_cc
147 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
147 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
148 assert issue.is_a?(Issue)
148 assert issue.is_a?(Issue)
149 assert !issue.new_record?
149 assert !issue.new_record?
150 issue.reload
150 issue.reload
151 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
151 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
152 assert_equal 1, issue.watchers.size
152 assert_equal 1, issue.watchers.size
153 end
153 end
154
154
155 def test_add_issue_by_unknown_user
155 def test_add_issue_by_unknown_user
156 assert_no_difference 'User.count' do
156 assert_no_difference 'User.count' do
157 assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'})
157 assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'})
158 end
158 end
159 end
159 end
160
160
161 def test_add_issue_by_anonymous_user
161 def test_add_issue_by_anonymous_user
162 Role.anonymous.add_permission!(:add_issues)
162 Role.anonymous.add_permission!(:add_issues)
163 assert_no_difference 'User.count' do
163 assert_no_difference 'User.count' do
164 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept')
164 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept')
165 assert issue.is_a?(Issue)
165 assert issue.is_a?(Issue)
166 assert issue.author.anonymous?
166 assert issue.author.anonymous?
167 end
167 end
168 end
168 end
169
170 def test_add_issue_by_anonymous_user_with_no_from_address
171 Role.anonymous.add_permission!(:add_issues)
172 assert_no_difference 'User.count' do
173 issue = submit_email('ticket_by_empty_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept')
174 assert issue.is_a?(Issue)
175 assert issue.author.anonymous?
176 end
177 end
169
178
170 def test_add_issue_by_anonymous_user_on_private_project
179 def test_add_issue_by_anonymous_user_on_private_project
171 Role.anonymous.add_permission!(:add_issues)
180 Role.anonymous.add_permission!(:add_issues)
172 assert_no_difference 'User.count' do
181 assert_no_difference 'User.count' do
173 assert_no_difference 'Issue.count' do
182 assert_no_difference 'Issue.count' do
174 assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :unknown_user => 'accept')
183 assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :unknown_user => 'accept')
175 end
184 end
176 end
185 end
177 end
186 end
178
187
179 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
188 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
180 assert_no_difference 'User.count' do
189 assert_no_difference 'User.count' do
181 assert_difference 'Issue.count' do
190 assert_difference 'Issue.count' do
182 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :no_permission_check => '1', :unknown_user => 'accept')
191 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :no_permission_check => '1', :unknown_user => 'accept')
183 assert issue.is_a?(Issue)
192 assert issue.is_a?(Issue)
184 assert issue.author.anonymous?
193 assert issue.author.anonymous?
185 assert !issue.project.is_public?
194 assert !issue.project.is_public?
186 end
195 end
187 end
196 end
188 end
197 end
189
198
190 def test_add_issue_by_created_user
199 def test_add_issue_by_created_user
191 Setting.default_language = 'en'
200 Setting.default_language = 'en'
192 assert_difference 'User.count' do
201 assert_difference 'User.count' do
193 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
202 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
194 assert issue.is_a?(Issue)
203 assert issue.is_a?(Issue)
195 assert issue.author.active?
204 assert issue.author.active?
196 assert_equal 'john.doe@somenet.foo', issue.author.mail
205 assert_equal 'john.doe@somenet.foo', issue.author.mail
197 assert_equal 'John', issue.author.firstname
206 assert_equal 'John', issue.author.firstname
198 assert_equal 'Doe', issue.author.lastname
207 assert_equal 'Doe', issue.author.lastname
199
208
200 # account information
209 # account information
201 email = ActionMailer::Base.deliveries.first
210 email = ActionMailer::Base.deliveries.first
202 assert_not_nil email
211 assert_not_nil email
203 assert email.subject.include?('account activation')
212 assert email.subject.include?('account activation')
204 login = email.body.match(/\* Login: (.*)$/)[1]
213 login = email.body.match(/\* Login: (.*)$/)[1]
205 password = email.body.match(/\* Password: (.*)$/)[1]
214 password = email.body.match(/\* Password: (.*)$/)[1]
206 assert_equal issue.author, User.try_to_login(login, password)
215 assert_equal issue.author, User.try_to_login(login, password)
207 end
216 end
208 end
217 end
209
218
210 def test_add_issue_without_from_header
219 def test_add_issue_without_from_header
211 Role.anonymous.add_permission!(:add_issues)
220 Role.anonymous.add_permission!(:add_issues)
212 assert_equal false, submit_email('ticket_without_from_header.eml')
221 assert_equal false, submit_email('ticket_without_from_header.eml')
213 end
222 end
214
223
215 def test_should_ignore_emails_from_emission_address
224 def test_should_ignore_emails_from_emission_address
216 Role.anonymous.add_permission!(:add_issues)
225 Role.anonymous.add_permission!(:add_issues)
217 assert_no_difference 'User.count' do
226 assert_no_difference 'User.count' do
218 assert_equal false, submit_email('ticket_from_emission_address.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
227 assert_equal false, submit_email('ticket_from_emission_address.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
219 end
228 end
220 end
229 end
221
230
222 def test_add_issue_should_send_email_notification
231 def test_add_issue_should_send_email_notification
223 ActionMailer::Base.deliveries.clear
232 ActionMailer::Base.deliveries.clear
224 # This email contains: 'Project: onlinestore'
233 # This email contains: 'Project: onlinestore'
225 issue = submit_email('ticket_on_given_project.eml')
234 issue = submit_email('ticket_on_given_project.eml')
226 assert issue.is_a?(Issue)
235 assert issue.is_a?(Issue)
227 assert_equal 1, ActionMailer::Base.deliveries.size
236 assert_equal 1, ActionMailer::Base.deliveries.size
228 end
237 end
229
238
230 def test_add_issue_note
239 def test_add_issue_note
231 journal = submit_email('ticket_reply.eml')
240 journal = submit_email('ticket_reply.eml')
232 assert journal.is_a?(Journal)
241 assert journal.is_a?(Journal)
233 assert_equal User.find_by_login('jsmith'), journal.user
242 assert_equal User.find_by_login('jsmith'), journal.user
234 assert_equal Issue.find(2), journal.journalized
243 assert_equal Issue.find(2), journal.journalized
235 assert_match /This is reply/, journal.notes
244 assert_match /This is reply/, journal.notes
236 end
245 end
237
246
238 def test_add_issue_note_with_status_change
247 def test_add_issue_note_with_status_change
239 # This email contains: 'Status: Resolved'
248 # This email contains: 'Status: Resolved'
240 journal = submit_email('ticket_reply_with_status.eml')
249 journal = submit_email('ticket_reply_with_status.eml')
241 assert journal.is_a?(Journal)
250 assert journal.is_a?(Journal)
242 issue = Issue.find(journal.issue.id)
251 issue = Issue.find(journal.issue.id)
243 assert_equal User.find_by_login('jsmith'), journal.user
252 assert_equal User.find_by_login('jsmith'), journal.user
244 assert_equal Issue.find(2), journal.journalized
253 assert_equal Issue.find(2), journal.journalized
245 assert_match /This is reply/, journal.notes
254 assert_match /This is reply/, journal.notes
246 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
255 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
247 end
256 end
248
257
249 def test_add_issue_note_should_send_email_notification
258 def test_add_issue_note_should_send_email_notification
250 ActionMailer::Base.deliveries.clear
259 ActionMailer::Base.deliveries.clear
251 journal = submit_email('ticket_reply.eml')
260 journal = submit_email('ticket_reply.eml')
252 assert journal.is_a?(Journal)
261 assert journal.is_a?(Journal)
253 assert_equal 1, ActionMailer::Base.deliveries.size
262 assert_equal 1, ActionMailer::Base.deliveries.size
254 end
263 end
255
264
256 def test_reply_to_a_message
265 def test_reply_to_a_message
257 m = submit_email('message_reply.eml')
266 m = submit_email('message_reply.eml')
258 assert m.is_a?(Message)
267 assert m.is_a?(Message)
259 assert !m.new_record?
268 assert !m.new_record?
260 m.reload
269 m.reload
261 assert_equal 'Reply via email', m.subject
270 assert_equal 'Reply via email', m.subject
262 # The email replies to message #2 which is part of the thread of message #1
271 # The email replies to message #2 which is part of the thread of message #1
263 assert_equal Message.find(1), m.parent
272 assert_equal Message.find(1), m.parent
264 end
273 end
265
274
266 def test_reply_to_a_message_by_subject
275 def test_reply_to_a_message_by_subject
267 m = submit_email('message_reply_by_subject.eml')
276 m = submit_email('message_reply_by_subject.eml')
268 assert m.is_a?(Message)
277 assert m.is_a?(Message)
269 assert !m.new_record?
278 assert !m.new_record?
270 m.reload
279 m.reload
271 assert_equal 'Reply to the first post', m.subject
280 assert_equal 'Reply to the first post', m.subject
272 assert_equal Message.find(1), m.parent
281 assert_equal Message.find(1), m.parent
273 end
282 end
274
283
275 def test_should_strip_tags_of_html_only_emails
284 def test_should_strip_tags_of_html_only_emails
276 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
285 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
277 assert issue.is_a?(Issue)
286 assert issue.is_a?(Issue)
278 assert !issue.new_record?
287 assert !issue.new_record?
279 issue.reload
288 issue.reload
280 assert_equal 'HTML email', issue.subject
289 assert_equal 'HTML email', issue.subject
281 assert_equal 'This is a html-only email.', issue.description
290 assert_equal 'This is a html-only email.', issue.description
282 end
291 end
283
292
284 context "truncate emails based on the Setting" do
293 context "truncate emails based on the Setting" do
285 context "with no setting" do
294 context "with no setting" do
286 setup do
295 setup do
287 Setting.mail_handler_body_delimiters = ''
296 Setting.mail_handler_body_delimiters = ''
288 end
297 end
289
298
290 should "add the entire email into the issue" do
299 should "add the entire email into the issue" do
291 issue = submit_email('ticket_on_given_project.eml')
300 issue = submit_email('ticket_on_given_project.eml')
292 assert_issue_created(issue)
301 assert_issue_created(issue)
293 assert issue.description.include?('---')
302 assert issue.description.include?('---')
294 assert issue.description.include?('This paragraph is after the delimiter')
303 assert issue.description.include?('This paragraph is after the delimiter')
295 end
304 end
296 end
305 end
297
306
298 context "with a single string" do
307 context "with a single string" do
299 setup do
308 setup do
300 Setting.mail_handler_body_delimiters = '---'
309 Setting.mail_handler_body_delimiters = '---'
301 end
310 end
302
311
303 should "truncate the email at the delimiter for the issue" do
312 should "truncate the email at the delimiter for the issue" do
304 issue = submit_email('ticket_on_given_project.eml')
313 issue = submit_email('ticket_on_given_project.eml')
305 assert_issue_created(issue)
314 assert_issue_created(issue)
306 assert issue.description.include?('This paragraph is before delimiters')
315 assert issue.description.include?('This paragraph is before delimiters')
307 assert issue.description.include?('--- This line starts with a delimiter')
316 assert issue.description.include?('--- This line starts with a delimiter')
308 assert !issue.description.match(/^---$/)
317 assert !issue.description.match(/^---$/)
309 assert !issue.description.include?('This paragraph is after the delimiter')
318 assert !issue.description.include?('This paragraph is after the delimiter')
310 end
319 end
311 end
320 end
312
321
313 context "with multiple strings" do
322 context "with multiple strings" do
314 setup do
323 setup do
315 Setting.mail_handler_body_delimiters = "---\nBREAK"
324 Setting.mail_handler_body_delimiters = "---\nBREAK"
316 end
325 end
317
326
318 should "truncate the email at the first delimiter found (BREAK)" do
327 should "truncate the email at the first delimiter found (BREAK)" do
319 issue = submit_email('ticket_on_given_project.eml')
328 issue = submit_email('ticket_on_given_project.eml')
320 assert_issue_created(issue)
329 assert_issue_created(issue)
321 assert issue.description.include?('This paragraph is before delimiters')
330 assert issue.description.include?('This paragraph is before delimiters')
322 assert !issue.description.include?('BREAK')
331 assert !issue.description.include?('BREAK')
323 assert !issue.description.include?('This paragraph is between delimiters')
332 assert !issue.description.include?('This paragraph is between delimiters')
324 assert !issue.description.match(/^---$/)
333 assert !issue.description.match(/^---$/)
325 assert !issue.description.include?('This paragraph is after the delimiter')
334 assert !issue.description.include?('This paragraph is after the delimiter')
326 end
335 end
327 end
336 end
328 end
337 end
329
338
330 private
339 private
331
340
332 def submit_email(filename, options={})
341 def submit_email(filename, options={})
333 raw = IO.read(File.join(FIXTURES_PATH, filename))
342 raw = IO.read(File.join(FIXTURES_PATH, filename))
334 MailHandler.receive(raw, options)
343 MailHandler.receive(raw, options)
335 end
344 end
336
345
337 def assert_issue_created(issue)
346 def assert_issue_created(issue)
338 assert issue.is_a?(Issue)
347 assert issue.is_a?(Issue)
339 assert !issue.new_record?
348 assert !issue.new_record?
340 issue.reload
349 issue.reload
341 end
350 end
342 end
351 end
General Comments 0
You need to be logged in to leave comments. Login now