@@ -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 |
|
|
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, |
|
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 = |
|
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}]", |
|
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}]", |
|
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, |
|
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 |
|
|
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?) |
|
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 |
|
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 |
|
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