Makes MailHandler accept all issue attributes and custom fields that can be set/updated (#4071, #4807, #5622, #6110).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4394 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
0eb7d8f614
commit
e0e8c14c2a
|
@ -248,19 +248,22 @@ class Issue < ActiveRecord::Base
|
||||||
def safe_attributes=(attrs, user=User.current)
|
def safe_attributes=(attrs, user=User.current)
|
||||||
return unless attrs.is_a?(Hash)
|
return unless attrs.is_a?(Hash)
|
||||||
|
|
||||||
new_statuses_allowed = new_statuses_allowed_to(user)
|
|
||||||
|
|
||||||
# User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
|
# User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
|
||||||
if new_record? || user.allowed_to?(:edit_issues, project)
|
if new_record? || user.allowed_to?(:edit_issues, project)
|
||||||
attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES.include?(k)}
|
attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES.include?(k)}
|
||||||
elsif new_statuses_allowed.any?
|
elsif new_statuses_allowed_to(user).any?
|
||||||
attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES_ON_TRANSITION.include?(k)}
|
attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES_ON_TRANSITION.include?(k)}
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Tracker must be set before since new_statuses_allowed_to depends on it.
|
||||||
|
if t = attrs.delete('tracker_id')
|
||||||
|
self.tracker_id = t
|
||||||
|
end
|
||||||
|
|
||||||
if attrs['status_id']
|
if attrs['status_id']
|
||||||
unless new_statuses_allowed.collect(&:id).include?(attrs['status_id'].to_i)
|
unless new_statuses_allowed_to(user).collect(&:id).include?(attrs['status_id'].to_i)
|
||||||
attrs.delete('status_id')
|
attrs.delete('status_id')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -116,36 +116,20 @@ class MailHandler < ActionMailer::Base
|
||||||
# Creates a new issue
|
# Creates a new issue
|
||||||
def receive_issue
|
def receive_issue
|
||||||
project = target_project
|
project = target_project
|
||||||
tracker = (get_keyword(:tracker) && project.trackers.find_by_name(get_keyword(:tracker))) || project.trackers.find(:first)
|
|
||||||
category = (get_keyword(:category) && project.issue_categories.find_by_name(get_keyword(:category)))
|
|
||||||
priority = (get_keyword(:priority) && IssuePriority.find_by_name(get_keyword(:priority)))
|
|
||||||
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
|
|
||||||
assigned_to = (get_keyword(:assigned_to, :override => true) && find_user_from_keyword(get_keyword(:assigned_to, :override => true)))
|
|
||||||
due_date = get_keyword(:due_date, :override => true)
|
|
||||||
start_date = get_keyword(:start_date, :override => true)
|
|
||||||
|
|
||||||
# check permission
|
# check permission
|
||||||
unless @@handler_options[:no_permission_check]
|
unless @@handler_options[:no_permission_check]
|
||||||
raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
|
raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
|
||||||
end
|
end
|
||||||
|
|
||||||
issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority, :due_date => due_date, :start_date => start_date, :assigned_to => assigned_to)
|
issue = Issue.new(:author => user, :project => project)
|
||||||
# check workflow
|
issue.safe_attributes = issue_attributes_from_keywords(issue)
|
||||||
if status && issue.new_statuses_allowed_to(user).include?(status)
|
issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
|
||||||
issue.status = status
|
issue.subject = email.subject.to_s.chomp[0,255]
|
||||||
end
|
|
||||||
issue.subject = email.subject.chomp[0,255]
|
|
||||||
if issue.subject.blank?
|
if issue.subject.blank?
|
||||||
issue.subject = '(no subject)'
|
issue.subject = '(no subject)'
|
||||||
end
|
end
|
||||||
# custom fields
|
|
||||||
issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
|
|
||||||
if value = get_keyword(c.name, :override => true)
|
|
||||||
h[c.id] = value
|
|
||||||
end
|
|
||||||
h
|
|
||||||
end
|
|
||||||
issue.description = cleaned_up_text_body
|
issue.description = cleaned_up_text_body
|
||||||
|
|
||||||
# add To and Cc as watchers before saving so the watchers can reply to Redmine
|
# add To and Cc as watchers before saving so the watchers can reply to Redmine
|
||||||
add_watchers(issue)
|
add_watchers(issue)
|
||||||
issue.save!
|
issue.save!
|
||||||
|
@ -154,41 +138,19 @@ class MailHandler < ActionMailer::Base
|
||||||
issue
|
issue
|
||||||
end
|
end
|
||||||
|
|
||||||
def target_project
|
|
||||||
# TODO: other ways to specify project:
|
|
||||||
# * parse the email To field
|
|
||||||
# * specific project (eg. Setting.mail_handler_target_project)
|
|
||||||
target = Project.find_by_identifier(get_keyword(:project))
|
|
||||||
raise MissingInformation.new('Unable to determine target project') if target.nil?
|
|
||||||
target
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds a note to an existing issue
|
# Adds a note to an existing issue
|
||||||
def receive_issue_reply(issue_id)
|
def receive_issue_reply(issue_id)
|
||||||
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
|
|
||||||
due_date = get_keyword(:due_date, :override => true)
|
|
||||||
start_date = get_keyword(:start_date, :override => true)
|
|
||||||
assigned_to = (get_keyword(:assigned_to, :override => true) && find_user_from_keyword(get_keyword(:assigned_to, :override => true)))
|
|
||||||
|
|
||||||
issue = Issue.find_by_id(issue_id)
|
issue = Issue.find_by_id(issue_id)
|
||||||
return unless issue
|
return unless issue
|
||||||
# check permission
|
# check permission
|
||||||
unless @@handler_options[:no_permission_check]
|
unless @@handler_options[:no_permission_check]
|
||||||
raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
|
raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
|
||||||
raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# add the note
|
|
||||||
journal = issue.init_journal(user, cleaned_up_text_body)
|
journal = issue.init_journal(user, cleaned_up_text_body)
|
||||||
|
issue.safe_attributes = issue_attributes_from_keywords(issue)
|
||||||
|
issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
|
||||||
add_attachments(issue)
|
add_attachments(issue)
|
||||||
# check workflow
|
|
||||||
if status && issue.new_statuses_allowed_to(user).include?(status)
|
|
||||||
issue.status = status
|
|
||||||
end
|
|
||||||
issue.start_date = start_date if start_date
|
|
||||||
issue.due_date = due_date if due_date
|
|
||||||
issue.assigned_to = assigned_to if assigned_to
|
|
||||||
|
|
||||||
issue.save!
|
issue.save!
|
||||||
logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
|
logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
|
||||||
journal
|
journal
|
||||||
|
@ -264,6 +226,41 @@ class MailHandler < ActionMailer::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def target_project
|
||||||
|
# TODO: other ways to specify project:
|
||||||
|
# * parse the email To field
|
||||||
|
# * specific project (eg. Setting.mail_handler_target_project)
|
||||||
|
target = Project.find_by_identifier(get_keyword(:project))
|
||||||
|
raise MissingInformation.new('Unable to determine target project') if target.nil?
|
||||||
|
target
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a Hash of issue attributes extracted from keywords in the email body
|
||||||
|
def issue_attributes_from_keywords(issue)
|
||||||
|
{
|
||||||
|
'tracker_id' => ((k = get_keyword(:tracker)) && issue.project.trackers.find_by_name(k).try(:id)) || issue.project.trackers.find(:first).try(:id),
|
||||||
|
'status_id' => (k = get_keyword(:status)) && IssueStatus.find_by_name(k).try(:id),
|
||||||
|
'priority_id' => (k = get_keyword(:priority)) && IssuePriority.find_by_name(k).try(:id),
|
||||||
|
'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.find_by_name(k).try(:id),
|
||||||
|
'assigned_to_id' => (k = get_keyword(:assigned_to, :override => true)) && find_user_from_keyword(k).try(:id),
|
||||||
|
'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && issue.project.shared_versions.find_by_name(k).try(:id),
|
||||||
|
'start_date' => get_keyword(:start_date, :override => true),
|
||||||
|
'due_date' => get_keyword(:due_date, :override => true),
|
||||||
|
'estimated_hours' => get_keyword(:estimated_hours, :override => true),
|
||||||
|
'done_ratio' => get_keyword(:done_ratio, :override => true),
|
||||||
|
}.delete_if {|k, v| v.blank? }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a Hash of issue custom field values extracted from keywords in the email body
|
||||||
|
def custom_field_values_from_keywords(customized)
|
||||||
|
customized.custom_field_values.inject({}) do |h, v|
|
||||||
|
if value = get_keyword(v.custom_field.name, :override => true)
|
||||||
|
h[v.custom_field.id.to_s] = value
|
||||||
|
end
|
||||||
|
h
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the text/plain part of the email
|
# Returns the text/plain part of the email
|
||||||
# If not found (eg. HTML-only email), returns the body with tags removed
|
# If not found (eg. HTML-only email), returns the body with tags removed
|
||||||
def plain_text_body
|
def plain_text_body
|
||||||
|
|
|
@ -54,4 +54,7 @@ Status: Resolved
|
||||||
due date: 2010-12-31
|
due date: 2010-12-31
|
||||||
Start Date:2010-01-01
|
Start Date:2010-01-01
|
||||||
Assigned to: John Smith
|
Assigned to: John Smith
|
||||||
|
fixed version: alpha
|
||||||
|
estimated hours: 2.5
|
||||||
|
done ratio: 30
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ Status: Resolved
|
||||||
due date: 2010-12-31
|
due date: 2010-12-31
|
||||||
Start Date:2010-01-01
|
Start Date:2010-01-01
|
||||||
Assigned to: jsmith@somenet.foo
|
Assigned to: jsmith@somenet.foo
|
||||||
|
searchable field: Updated custom value
|
||||||
|
|
||||||
------=_NextPart_000_0067_01C8D3CE.711F9CC0
|
------=_NextPart_000_0067_01C8D3CE.711F9CC0
|
||||||
Content-Type: text/html;
|
Content-Type: text/html;
|
||||||
|
|
|
@ -30,6 +30,7 @@ class MailHandlerTest < ActiveSupport::TestCase
|
||||||
:workflows,
|
:workflows,
|
||||||
:trackers,
|
:trackers,
|
||||||
:projects_trackers,
|
:projects_trackers,
|
||||||
|
:versions,
|
||||||
:enumerations,
|
:enumerations,
|
||||||
:issue_categories,
|
:issue_categories,
|
||||||
:custom_fields,
|
:custom_fields,
|
||||||
|
@ -59,6 +60,9 @@ class MailHandlerTest < ActiveSupport::TestCase
|
||||||
assert_equal '2010-01-01', issue.start_date.to_s
|
assert_equal '2010-01-01', issue.start_date.to_s
|
||||||
assert_equal '2010-12-31', issue.due_date.to_s
|
assert_equal '2010-12-31', issue.due_date.to_s
|
||||||
assert_equal User.find_by_login('jsmith'), issue.assigned_to
|
assert_equal User.find_by_login('jsmith'), issue.assigned_to
|
||||||
|
assert_equal Version.find_by_name('alpha'), issue.fixed_version
|
||||||
|
assert_equal 2.5, issue.estimated_hours
|
||||||
|
assert_equal 30, issue.done_ratio
|
||||||
# keywords should be removed from the email body
|
# keywords should be removed from the email body
|
||||||
assert !issue.description.match(/^Project:/i)
|
assert !issue.description.match(/^Project:/i)
|
||||||
assert !issue.description.match(/^Status:/i)
|
assert !issue.description.match(/^Status:/i)
|
||||||
|
@ -269,6 +273,7 @@ class MailHandlerTest < ActiveSupport::TestCase
|
||||||
assert_equal '2010-01-01', issue.start_date.to_s
|
assert_equal '2010-01-01', issue.start_date.to_s
|
||||||
assert_equal '2010-12-31', issue.due_date.to_s
|
assert_equal '2010-12-31', issue.due_date.to_s
|
||||||
assert_equal User.find_by_login('jsmith'), issue.assigned_to
|
assert_equal User.find_by_login('jsmith'), issue.assigned_to
|
||||||
|
assert_equal 'Updated custom value', issue.custom_value_for(CustomField.find_by_name('Searchable field')).value
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_issue_note_should_send_email_notification
|
def test_add_issue_note_should_send_email_notification
|
||||||
|
|
Loading…
Reference in New Issue