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