Patch #4409 ยป 0001-Remove-the-body-of-incoming-emails-after-specific-de.patch
| app/models/mail_handler.rb | ||
|---|---|---|
| 81 | 81 |
MESSAGE_ID_RE = %r{^<redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
|
| 82 | 82 |
ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]}
|
| 83 | 83 |
MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
|
| 84 |
|
|
| 84 | ||
| 85 | 85 |
def dispatch |
| 86 | 86 |
headers = [email.in_reply_to, email.references].flatten.compact |
| 87 | 87 |
if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
|
| ... | ... | |
| 131 | 131 |
if issue.subject.blank? |
| 132 | 132 |
issue.subject = '(no subject)' |
| 133 | 133 |
end |
| 134 |
issue.description = plain_text_body |
|
| 135 | 134 |
# custom fields |
| 136 | 135 |
issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
|
| 137 | 136 |
if value = get_keyword(c.name, :override => true) |
| ... | ... | |
| 139 | 138 |
end |
| 140 | 139 |
h |
| 141 | 140 |
end |
| 141 |
issue.description = cleanup_body(plain_text_body) |
|
| 142 | 142 |
# add To and Cc as watchers before saving so the watchers can reply to Redmine |
| 143 | 143 |
add_watchers(issue) |
| 144 | 144 |
issue.save! |
| ... | ... | |
| 167 | 167 |
raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project) |
| 168 | 168 | |
| 169 | 169 |
# add the note |
| 170 |
journal = issue.init_journal(user, plain_text_body)
|
|
| 170 |
journal = issue.init_journal(user, cleanup_body(plain_text_body))
|
|
| 171 | 171 |
add_attachments(issue) |
| 172 | 172 |
# check workflow |
| 173 | 173 |
if status && issue.new_statuses_allowed_to(user).include?(status) |
| ... | ... | |
| 242 | 242 |
end |
| 243 | 243 |
end |
| 244 | 244 |
end |
| 245 |
|
|
| 245 | ||
| 246 | 246 |
# Returns the text/plain part of the email |
| 247 | 247 |
# If not found (eg. HTML-only email), returns the body with tags removed |
| 248 | 248 |
def plain_text_body |
| ... | ... | |
| 287 | 287 |
user.save ? user : nil |
| 288 | 288 |
end |
| 289 | 289 |
end |
| 290 | ||
| 291 |
private |
|
| 292 |
|
|
| 293 |
# Removes the email body of text after the truncation configurations. |
|
| 294 |
def cleanup_body(body) |
|
| 295 |
if Setting.emails_truncate.present? |
|
| 296 |
truncate_tokens = Setting.emails_truncate.split("\n")
|
|
| 297 | ||
| 298 |
truncate_tokens.each do |token| |
|
| 299 |
next if token.blank? |
|
| 300 |
|
|
| 301 |
regex = Regexp.new(token + '.*', Regexp::IGNORECASE | Regexp::MULTILINE) |
|
| 302 |
body.gsub!(regex, '') |
|
| 303 |
end |
|
| 304 |
end |
|
| 305 |
|
|
| 306 |
return body.chomp |
|
| 307 |
end |
|
| 290 | 308 |
end |
| app/views/settings/_mail_handler.rhtml | ||
|---|---|---|
| 1 | 1 |
<% form_tag({:action => 'edit', :tab => 'mail_handler'}) do %>
|
| 2 | 2 | |
| 3 | 3 |
<div class="box tabular settings"> |
| 4 |
<p> |
|
| 5 |
<label><%= l(:setting_emails_truncate) %></label> |
|
| 6 |
<%= text_area_tag 'settings[emails_truncate]', Setting.emails_truncate, :rows => 5, :cols => 80 %> |
|
| 7 |
<br /><em><%= l(:text_line_separated) %></em> |
|
| 8 |
</p> |
|
| 9 |
</div> |
|
| 10 | ||
| 11 |
<div class="box tabular settings"> |
|
| 4 | 12 |
<p><label><%= l(:setting_mail_handler_api_enabled) %></label> |
| 5 | 13 |
<%= hidden_field_tag 'settings[mail_handler_api_enabled]', 0 %> |
| 6 | 14 |
<%= check_box_tag 'settings[mail_handler_api_enabled]', 1, Setting.mail_handler_api_enabled?, |
| ... | ... | |
| 16 | 24 |
</div> |
| 17 | 25 | |
| 18 | 26 |
<%= submit_tag l(:button_save) %> |
| 27 | ||
| 19 | 28 |
<% end %> |
| config/locales/en.yml | ||
|---|---|---|
| 323 | 323 |
setting_issue_done_ratio_issue_field: Use the issue field |
| 324 | 324 |
setting_issue_done_ratio_issue_status: Use the issue status |
| 325 | 325 |
setting_start_of_week: Start calendars on |
| 326 |
setting_emails_truncate: "Truncate emails after the following strings" |
|
| 326 | 327 |
|
| 327 | 328 |
permission_add_project: Create project |
| 328 | 329 |
permission_edit_project: Edit project |
| ... | ... | |
| 801 | 802 |
text_tracker_no_workflow: No workflow defined for this tracker |
| 802 | 803 |
text_unallowed_characters: Unallowed characters |
| 803 | 804 |
text_comma_separated: Multiple values allowed (comma separated). |
| 805 |
text_line_separated: Multiple values allowed (line separated). |
|
| 804 | 806 |
text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages |
| 805 | 807 |
text_issue_added: "Issue {{id}} has been reported by {{author}}."
|
| 806 | 808 |
text_issue_updated: "Issue {{id}} has been updated by {{author}}."
|
| config/settings.yml | ||
|---|---|---|
| 174 | 174 |
default: '' |
| 175 | 175 |
start_of_week: |
| 176 | 176 |
default: '' |
| 177 |
emails_truncate: |
|
| 178 |
default: '' |
|
| test/fixtures/mail_handler/ticket_on_given_project.eml | ||
|---|---|---|
| 30 | 30 |
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse |
| 31 | 31 |
platea dictumst. |
| 32 | 32 |
|
| 33 |
--- |
|
| 34 |
|
|
| 35 |
This paragraph is after the delimiter so it shouldn't appear. |
|
| 36 |
|
|
| 33 | 37 |
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque |
| 34 | 38 |
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem. |
| 35 | 39 |
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et, |
| test/fixtures/mail_handler/ticket_reply.eml | ||
|---|---|---|
| 25 | 25 |
Content-Transfer-Encoding: quoted-printable |
| 26 | 26 |
|
| 27 | 27 |
This is reply |
| 28 |
|
|
| 29 |
--- |
|
| 30 |
|
|
| 31 |
This is my sig. |
|
| 32 |
|
|
| 28 | 33 |
------=_NextPart_000_0067_01C8D3CE.711F9CC0 |
| 29 | 34 |
Content-Type: text/html; |
| 30 | 35 |
charset="utf-8" |
| test/unit/mail_handler_test.rb | ||
|---|---|---|
| 204 | 204 |
assert issue.is_a?(Issue) |
| 205 | 205 |
assert_equal 1, ActionMailer::Base.deliveries.size |
| 206 | 206 |
end |
| 207 | ||
| 208 |
context "truncate emails based on the Setting" do |
|
| 209 |
context "with no setting" do |
|
| 210 |
setup do |
|
| 211 |
Setting.emails_truncate = '' |
|
| 212 |
end |
|
| 213 | ||
| 214 |
should "add the entire email into the issue" do |
|
| 215 |
issue = submit_email('ticket_on_given_project.eml')
|
|
| 216 | ||
| 217 |
assert_issue_created(issue) |
|
| 218 |
assert issue.description.include?('---')
|
|
| 219 |
assert issue.description.include?('This paragraph is after the delimiter')
|
|
| 220 |
end |
|
| 221 |
end |
|
| 222 | ||
| 223 |
context "with a single string" do |
|
| 224 |
setup do |
|
| 225 |
Setting.emails_truncate = '---' |
|
| 226 |
end |
|
| 227 | ||
| 228 |
should "truncate the email at the delimiter for the issue" do |
|
| 229 |
issue = submit_email('ticket_on_given_project.eml')
|
|
| 230 | ||
| 231 |
assert_issue_created(issue) |
|
| 232 |
assert !issue.description.include?('---')
|
|
| 233 |
assert !issue.description.include?('This paragraph is after the delimiter')
|
|
| 234 |
assert issue.description.include?('platea dictumst.')
|
|
| 235 |
end |
|
| 236 | ||
| 237 |
end |
|
| 238 | ||
| 239 |
context "with multiple strings" do |
|
| 240 |
setup do |
|
| 241 |
Setting.emails_truncate = "---\nBREAK\nid" |
|
| 242 |
end |
|
| 243 | ||
| 244 |
should "truncate the email at the first delimiter found (id)" do |
|
| 245 |
issue = submit_email('ticket_on_given_project.eml')
|
|
| 246 | ||
| 247 |
assert_issue_created(issue) |
|
| 248 |
assert !issue.description.include?('---')
|
|
| 249 |
assert !issue.description.include?('This paragraph is after the delimiter')
|
|
| 250 |
assert !issue.description.include?('platea dictumst.')
|
|
| 251 |
assert issue.description.include?('blandit eleifend augue. Nulla facilisi. Duis ')
|
|
| 252 |
end |
|
| 253 | ||
| 254 |
end |
|
| 255 | ||
| 256 |
|
|
| 257 |
end |
|
| 207 | 258 |
|
| 259 |
def test_add_issue_with_strip_delimiter_should_not_have_text_after_delimiter |
|
| 260 |
with_settings :emails_truncate => '---' do |
|
| 261 |
# This email contains: 'Project: onlinestore' |
|
| 262 |
issue = submit_email('ticket_on_given_project.eml')
|
|
| 263 |
assert issue.is_a?(Issue) |
|
| 264 |
assert !issue.new_record? |
|
| 265 |
issue.reload |
|
| 266 |
assert_equal 'New ticket on a given project', issue.subject |
|
| 267 |
assert !issue.description.include?("This paragraph is after the delimiter so it shouldn't appear."), "Delimited text was not stripped"
|
|
| 268 |
assert !issue.description.include?("---"), "Delimiter was not stripped"
|
|
| 269 |
end |
|
| 270 |
end |
|
| 271 | ||
| 272 |
def test_add_issue_with_strip_delimiter_should_not_have_delimiter |
|
| 273 |
with_settings :emails_truncate => '---' do |
|
| 274 |
# This email contains: 'Project: onlinestore' |
|
| 275 |
issue = submit_email('ticket_on_given_project.eml')
|
|
| 276 |
assert issue.is_a?(Issue) |
|
| 277 |
assert !issue.new_record? |
|
| 278 |
issue.reload |
|
| 279 |
assert_equal 'New ticket on a given project', issue.subject |
|
| 280 |
assert !issue.description.include?("---"), "Delimiter was not stripped"
|
|
| 281 |
end |
|
| 282 |
end |
|
| 283 | ||
| 208 | 284 |
def test_add_issue_note |
| 209 | 285 |
journal = submit_email('ticket_reply.eml')
|
| 210 | 286 |
assert journal.is_a?(Journal) |
| ... | ... | |
| 259 | 335 |
assert_equal 'This is a html-only email.', issue.description |
| 260 | 336 |
end |
| 261 | 337 | |
| 338 |
def test_add_issue_note_with_strip_delimiter_should_not_have_text_after_delimiter |
|
| 339 |
with_settings :emails_truncate => '---' do |
|
| 340 |
journal = submit_email('ticket_reply.eml')
|
|
| 341 |
assert journal.is_a?(Journal) |
|
| 342 |
assert !journal.notes.include?("This is my sig"), "Delimited text was not stripped"
|
|
| 343 |
end |
|
| 344 |
end |
|
| 345 |
|
|
| 346 |
def test_add_issue_note_with_strip_delimiter_should_not_have_delimiter |
|
| 347 |
with_settings :emails_truncate => '---' do |
|
| 348 |
journal = submit_email('ticket_reply.eml')
|
|
| 349 |
assert journal.is_a?(Journal) |
|
| 350 |
assert !journal.notes.include?("---"), "Delimiter was not stripped"
|
|
| 351 |
end |
|
| 352 |
end |
|
| 353 |
|
|
| 262 | 354 |
private |
| 263 | 355 |
|
| 264 | 356 |
def submit_email(filename, options={})
|
| 265 | 357 |
raw = IO.read(File.join(FIXTURES_PATH, filename)) |
| 266 | 358 |
MailHandler.receive(raw, options) |
| 267 | 359 |
end |
| 360 | ||
| 361 |
def assert_issue_created(issue) |
|
| 362 |
assert issue.is_a?(Issue) |
|
| 363 |
assert !issue.new_record? |
|
| 364 |
issue.reload |
|
| 365 |
end |
|
| 268 | 366 |
end |