##// END OF EJS Templates
Optional Regex delimiters to truncate incoming emails (#5864)....
Jean-Philippe Lang -
r15683:f30462595e83
parent child
Show More
@@ -0,0 +1,35
1 Return-Path: <jsmith@somenet.foo>
2 Received: from osiris ([127.0.0.1])
3 by OSIRIS
4 with hMailServer; Wed, 12 Oct 2016 03:05:50 -0700
5 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
6 From: "John Smith" <JSmith@somenet.foo>
7 To: <redmine@somenet.foo>
8 Subject: New ticket on a given project
9 Date: Wed, 12 Oct 2016 13:05:38 +0300
10 MIME-Version: 1.0
11 Content-Type: text/plain;
12 format=flowed;
13 charset="iso-8859-1";
14 reply-type=original
15 Content-Transfer-Encoding: 7bit
16 X-Priority: 3
17 X-MSMail-Priority: Normal
18 X-Mailer: Microsoft Outlook Express 6.00.2900.2869
19 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
20
21 Project: onlinestore
22 Status: Resolved
23 due date: 2010-12-31
24 Start Date:2010-01-01
25 Assigned to: John Smith
26 fixed version: alpha
27 estimated hours: 2.5
28 remaining hours: 1
29 done ratio: 30
30
31 This paragraph is before delimiter
32
33 On Wed, 11 Oct at 1:05 PM, Jon Smith <jsmith@somenet.foo<mailto:jsmith@somenet.foo>> wrote:
34
35 This paragraph is after the delimiter
@@ -34,24 +34,29 class SettingsController < ApplicationController
34 def edit
34 def edit
35 @notifiables = Redmine::Notifiable.all
35 @notifiables = Redmine::Notifiable.all
36 if request.post?
36 if request.post?
37 if Setting.set_all_from_params(params[:settings])
37 errors = Setting.set_all_from_params(params[:settings])
38 if errors.blank?
38 flash[:notice] = l(:notice_successful_update)
39 flash[:notice] = l(:notice_successful_update)
40 redirect_to settings_path(:tab => params[:tab])
41 return
42 else
43 @setting_errors = errors
44 # render the edit form with error messages
39 end
45 end
40 redirect_to settings_path(:tab => params[:tab])
46 end
41 else
42 @options = {}
43 user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]}
44 @options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]}
45 @deliveries = ActionMailer::Base.perform_deliveries
46
47
47 @guessed_host_and_path = request.host_with_port.dup
48 @options = {}
48 @guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
49 user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]}
50 @options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]}
51 @deliveries = ActionMailer::Base.perform_deliveries
49
52
50 @commit_update_keywords = Setting.commit_update_keywords.dup
53 @guessed_host_and_path = request.host_with_port.dup
51 @commit_update_keywords = [{}] unless @commit_update_keywords.is_a?(Array) && @commit_update_keywords.any?
54 @guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
52
55
53 Redmine::Themes.rescan
56 @commit_update_keywords = Setting.commit_update_keywords.dup
54 end
57 @commit_update_keywords = [{}] unless @commit_update_keywords.is_a?(Array) && @commit_update_keywords.any?
58
59 Redmine::Themes.rescan
55 end
60 end
56
61
57 def plugin
62 def plugin
@@ -32,18 +32,35 module SettingsHelper
32 ]
32 ]
33 end
33 end
34
34
35 def render_settings_error(errors)
36 return if errors.blank?
37 s = ''.html_safe
38 errors.each do |name, message|
39 s << content_tag('li', content_tag('b', l("setting_#{name}")) + " " + message)
40 end
41 content_tag('div', content_tag('ul', s), :id => 'errorExplanation')
42 end
43
44 def setting_value(setting)
45 value = nil
46 if params[:settings]
47 value = params[:settings][setting]
48 end
49 value || Setting.send(setting)
50 end
51
35 def setting_select(setting, choices, options={})
52 def setting_select(setting, choices, options={})
36 if blank_text = options.delete(:blank)
53 if blank_text = options.delete(:blank)
37 choices = [[blank_text.is_a?(Symbol) ? l(blank_text) : blank_text, '']] + choices
54 choices = [[blank_text.is_a?(Symbol) ? l(blank_text) : blank_text, '']] + choices
38 end
55 end
39 setting_label(setting, options).html_safe +
56 setting_label(setting, options).html_safe +
40 select_tag("settings[#{setting}]",
57 select_tag("settings[#{setting}]",
41 options_for_select(choices, Setting.send(setting).to_s),
58 options_for_select(choices, setting_value(setting).to_s),
42 options).html_safe
59 options).html_safe
43 end
60 end
44
61
45 def setting_multiselect(setting, choices, options={})
62 def setting_multiselect(setting, choices, options={})
46 setting_values = Setting.send(setting)
63 setting_values = setting_value(setting)
47 setting_values = [] unless setting_values.is_a?(Array)
64 setting_values = [] unless setting_values.is_a?(Array)
48
65
49 content_tag("label", l(options[:label] || "setting_#{setting}")) +
66 content_tag("label", l(options[:label] || "setting_#{setting}")) +
@@ -65,18 +82,18 module SettingsHelper
65
82
66 def setting_text_field(setting, options={})
83 def setting_text_field(setting, options={})
67 setting_label(setting, options).html_safe +
84 setting_label(setting, options).html_safe +
68 text_field_tag("settings[#{setting}]", Setting.send(setting), options).html_safe
85 text_field_tag("settings[#{setting}]", setting_value(setting), options).html_safe
69 end
86 end
70
87
71 def setting_text_area(setting, options={})
88 def setting_text_area(setting, options={})
72 setting_label(setting, options).html_safe +
89 setting_label(setting, options).html_safe +
73 text_area_tag("settings[#{setting}]", Setting.send(setting), options).html_safe
90 text_area_tag("settings[#{setting}]", setting_value(setting), options).html_safe
74 end
91 end
75
92
76 def setting_check_box(setting, options={})
93 def setting_check_box(setting, options={})
77 setting_label(setting, options).html_safe +
94 setting_label(setting, options).html_safe +
78 hidden_field_tag("settings[#{setting}]", 0, :id => nil).html_safe +
95 hidden_field_tag("settings[#{setting}]", 0, :id => nil).html_safe +
79 check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options).html_safe
96 check_box_tag("settings[#{setting}]", 1, setting_value(setting).to_s != '0', options).html_safe
80 end
97 end
81
98
82 def setting_label(setting, options={})
99 def setting_label(setting, options={})
@@ -97,7 +114,7 module SettingsHelper
97
114
98 tag = check_box_tag('settings[notified_events][]',
115 tag = check_box_tag('settings[notified_events][]',
99 notifiable.name,
116 notifiable.name,
100 Setting.notified_events.include?(notifiable.name),
117 setting_value('notified_events').include?(notifiable.name),
101 :id => nil,
118 :id => nil,
102 :data => tag_data)
119 :data => tag_data)
103
120
@@ -561,9 +561,18 class MailHandler < ActionMailer::Base
561
561
562 # Removes the email body of text after the truncation configurations.
562 # Removes the email body of text after the truncation configurations.
563 def cleanup_body(body)
563 def cleanup_body(body)
564 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
564 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?)
565
566 if Setting.mail_handler_enable_regex_delimiters?
567 begin
568 delimiters = delimiters.map {|s| Regexp.new(s)}
569 rescue RegexpError => e
570 logger.error "MailHandler: invalid regexp delimiter found in mail_handler_body_delimiters setting (#{e.message})" if logger
571 end
572 end
573
565 unless delimiters.empty?
574 unless delimiters.empty?
566 regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
575 regex = Regexp.new("^[> ]*(#{ Regexp.union(delimiters) })\s*[\r\n].*", Regexp::MULTILINE)
567 body = body.gsub(regex, '')
576 body = body.gsub(regex, '')
568 end
577 end
569 body.strip
578 body.strip
@@ -120,8 +120,12 class Setting < ActiveRecord::Base
120
120
121 # Updates multiple settings from params and sends a security notification if needed
121 # Updates multiple settings from params and sends a security notification if needed
122 def self.set_all_from_params(settings)
122 def self.set_all_from_params(settings)
123 return false unless settings.is_a?(Hash)
123 return nil unless settings.is_a?(Hash)
124 settings = settings.dup.symbolize_keys
124 settings = settings.dup.symbolize_keys
125
126 errors = validate_all_from_params(settings)
127 return errors if errors.present?
128
125 changes = []
129 changes = []
126 settings.each do |name, value|
130 settings.each do |name, value|
127 next unless available_settings[name.to_s]
131 next unless available_settings[name.to_s]
@@ -134,7 +138,29 class Setting < ActiveRecord::Base
134 if changes.any?
138 if changes.any?
135 Mailer.security_settings_updated(changes)
139 Mailer.security_settings_updated(changes)
136 end
140 end
137 true
141 nil
142 end
143
144 def self.validate_all_from_params(settings)
145 messages = []
146
147 if settings.key?(:mail_handler_body_delimiters) || settings.key?(:mail_handler_enable_regex_delimiters)
148 regexp = Setting.mail_handler_enable_regex_delimiters?
149 if settings.key?(:mail_handler_enable_regex_delimiters)
150 regexp = settings[:mail_handler_enable_regex_delimiters].to_s != '0'
151 end
152 if regexp
153 settings[:mail_handler_body_delimiters].to_s.split(/[\r\n]+/).each do |delimiter|
154 begin
155 Regexp.new(delimiter)
156 rescue RegexpError => e
157 messages << [:mail_handler_body_delimiters, "#{l('activerecord.errors.messages.not_a_regexp')} (#{e.message})"]
158 end
159 end
160 end
161 end
162
163 messages
138 end
164 end
139
165
140 # Sets a setting value from params
166 # Sets a setting value from params
@@ -3,6 +3,7
3 <div class="box tabular settings">
3 <div class="box tabular settings">
4 <p>
4 <p>
5 <%= setting_text_area :mail_handler_body_delimiters, :rows => 5 %>
5 <%= setting_text_area :mail_handler_body_delimiters, :rows => 5 %>
6 <%= setting_check_box :mail_handler_enable_regex_delimiters, :label => false %> <%= l(:setting_mail_handler_enable_regex_delimiters) %>
6 <em class="info"><%= l(:text_line_separated) %></em>
7 <em class="info"><%= l(:text_line_separated) %></em>
7 </p>
8 </p>
8 <p>
9 <p>
@@ -1,5 +1,7
1 <h2><%= l(:label_settings) %></h2>
1 <h2><%= l(:label_settings) %></h2>
2
2
3 <%= render_settings_error @setting_errors %>
4
3 <%= render_tabs administration_settings_tabs %>
5 <%= render_tabs administration_settings_tabs %>
4
6
5 <% html_title(l(:label_settings), l(:label_administration)) -%>
7 <% html_title(l(:label_settings), l(:label_administration)) -%>
@@ -130,6 +130,7 en:
130 circular_dependency: "This relation would create a circular dependency"
130 circular_dependency: "This relation would create a circular dependency"
131 cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
131 cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
132 earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
132 earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
133 not_a_regexp: "is not a valid regular expression"
133
134
134 actionview_instancetag_blank_option: Please select
135 actionview_instancetag_blank_option: Please select
135
136
@@ -403,6 +404,7 en:
403 setting_display_subprojects_issues: Display subprojects issues on main projects by default
404 setting_display_subprojects_issues: Display subprojects issues on main projects by default
404 setting_enabled_scm: Enabled SCM
405 setting_enabled_scm: Enabled SCM
405 setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
406 setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
407 setting_mail_handler_enable_regex_delimiters: "Enable regular expressions"
406 setting_mail_handler_api_enabled: Enable WS for incoming emails
408 setting_mail_handler_api_enabled: Enable WS for incoming emails
407 setting_mail_handler_api_key: Incoming email WS API key
409 setting_mail_handler_api_key: Incoming email WS API key
408 setting_sys_api_key: Repository management WS API key
410 setting_sys_api_key: Repository management WS API key
@@ -150,6 +150,7 fr:
150 circular_dependency: "Cette relation créerait une dépendance circulaire"
150 circular_dependency: "Cette relation créerait une dépendance circulaire"
151 cant_link_an_issue_with_a_descendant: "Une demande ne peut pas être liée à l'une de ses sous-tâches"
151 cant_link_an_issue_with_a_descendant: "Une demande ne peut pas être liée à l'une de ses sous-tâches"
152 earlier_than_minimum_start_date: "ne peut pas être antérieure au %{date} à cause des demandes qui précèdent"
152 earlier_than_minimum_start_date: "ne peut pas être antérieure au %{date} à cause des demandes qui précèdent"
153 not_a_regexp: "n'est pas une expression regulière valide"
153
154
154 actionview_instancetag_blank_option: Choisir
155 actionview_instancetag_blank_option: Choisir
155
156
@@ -415,6 +416,7 fr:
415 setting_display_subprojects_issues: Afficher par défaut les demandes des sous-projets sur les projets principaux
416 setting_display_subprojects_issues: Afficher par défaut les demandes des sous-projets sur les projets principaux
416 setting_enabled_scm: SCM activés
417 setting_enabled_scm: SCM activés
417 setting_mail_handler_body_delimiters: "Tronquer les emails après l'une de ces lignes"
418 setting_mail_handler_body_delimiters: "Tronquer les emails après l'une de ces lignes"
419 setting_mail_handler_enable_regex_delimiters: "Utiliser les expressions regulières"
418 setting_mail_handler_api_enabled: "Activer le WS pour la réception d'emails"
420 setting_mail_handler_api_enabled: "Activer le WS pour la réception d'emails"
419 setting_mail_handler_api_key: Clé de protection de l'API
421 setting_mail_handler_api_key: Clé de protection de l'API
420 setting_sequential_project_identifiers: Générer des identifiants de projet séquentiels
422 setting_sequential_project_identifiers: Générer des identifiants de projet séquentiels
@@ -182,6 +182,8 notified_events:
182 - issue_updated
182 - issue_updated
183 mail_handler_body_delimiters:
183 mail_handler_body_delimiters:
184 default: ''
184 default: ''
185 mail_handler_enable_regex_delimiters:
186 default: 0
185 mail_handler_excluded_filenames:
187 mail_handler_excluded_filenames:
186 default: ''
188 default: ''
187 mail_handler_api_enabled:
189 mail_handler_api_enabled:
@@ -254,4 +254,33 class SettingsControllerTest < Redmine::ControllerTest
254 ensure
254 ensure
255 Redmine::Plugin.unregister(:foo)
255 Redmine::Plugin.unregister(:foo)
256 end
256 end
257
258 def test_post_mail_handler_delimiters_should_not_save_invalid_regex_delimiters
259 post :edit, :params => {
260 :settings => {
261 :mail_handler_enable_regex_delimiters => '1',
262 :mail_handler_body_delimiters => 'Abc[',
263 }
264 }
265
266 assert_response :success
267 assert_equal '0', Setting.mail_handler_enable_regex_delimiters
268 assert_equal '', Setting.mail_handler_body_delimiters
269
270 assert_select_error /is not a valid regular expression/
271 assert_select 'textarea[name=?]', 'settings[mail_handler_body_delimiters]', :text => 'Abc['
272 end
273
274 def test_post_mail_handler_delimiters_should_save_valid_regex_delimiters
275 post :edit, :params => {
276 :settings => {
277 :mail_handler_enable_regex_delimiters => '1',
278 :mail_handler_body_delimiters => 'On .*, .* at .*, .* <.*<mailto:.*>> wrote:',
279 }
280 }
281
282 assert_redirected_to '/settings'
283 assert_equal '1', Setting.mail_handler_enable_regex_delimiters
284 assert_equal 'On .*, .* at .*, .* <.*<mailto:.*>> wrote:', Setting.mail_handler_body_delimiters
285 end
257 end
286 end
@@ -977,6 +977,25 class MailHandlerTest < ActiveSupport::TestCase
977 end
977 end
978 end
978 end
979
979
980 test "truncate emails using a regex delimiter" do
981 delimiter = "On .*, .* at .*, .* <.*<mailto:.*>> wrote:"
982 with_settings :mail_handler_enable_regex_delimiters => '1', :mail_handler_body_delimiters => delimiter do
983 issue = submit_email('ticket_reply_from_mail.eml')
984 assert_issue_created(issue)
985 assert issue.description.include?('This paragraph is before delimiter')
986 assert !issue.description.include?('On Wed, 11 Oct at 1:05 PM, Jon Smith <jsmith@somenet.foo<mailto:jsmith@somenet.foo>> wrote:')
987 assert !issue.description.include?('This paragraph is after the delimiter')
988 end
989
990 with_settings :mail_handler_enable_regex_delimiters => '0', :mail_handler_body_delimiters => delimiter do
991 issue = submit_email('ticket_reply_from_mail.eml')
992 assert_issue_created(issue)
993 assert issue.description.include?('This paragraph is before delimiter')
994 assert issue.description.include?('On Wed, 11 Oct at 1:05 PM, Jon Smith <jsmith@somenet.foo<mailto:jsmith@somenet.foo>> wrote:')
995 assert issue.description.include?('This paragraph is after the delimiter')
996 end
997 end
998
980 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
999 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
981 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
1000 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
982 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
1001 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
General Comments 0
You need to be logged in to leave comments. Login now