summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/models/mail_handler.rb33
-rw-r--r--extra/mail_handler/rdm-mailhandler.rb65
-rw-r--r--lib/tasks/email.rake76
-rw-r--r--test/fixtures/issue_categories.yml6
-rw-r--r--test/fixtures/mail_handler/ticket_with_attributes.eml43
-rw-r--r--test/unit/mail_handler_test.rb40
6 files changed, 214 insertions, 49 deletions
diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb
index 124f7db74..109db2989 100644
--- a/app/models/mail_handler.rb
+++ b/app/models/mail_handler.rb
@@ -23,7 +23,14 @@ class MailHandler < ActionMailer::Base
attr_reader :email, :user
def self.receive(email, options={})
- @@handler_options = options
+ @@handler_options = options.dup
+
+ @@handler_options[:issue] ||= {}
+
+ @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) if @@handler_options[:allow_override].is_a?(String)
+ @@handler_options[:allow_override] ||= []
+ # Project needs to be overridable if not specified
+ @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
super email
end
@@ -66,11 +73,13 @@ class MailHandler < ActionMailer::Base
# Creates a new issue
def receive_issue
project = target_project
- # TODO: make the tracker configurable
- tracker = project.trackers.find(:first)
+ 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) && Enumeration.find_by_opt_and_name('IPRI', get_keyword(:priority)))
+
# check permission
raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
- issue = Issue.new(:author => user, :project => project, :tracker => tracker)
+ issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority)
issue.subject = email.subject.chomp
issue.description = email.plain_text_body.chomp
issue.save!
@@ -84,13 +93,7 @@ class MailHandler < ActionMailer::Base
# TODO: other ways to specify project:
# * parse the email To field
# * specific project (eg. Setting.mail_handler_target_project)
- identifier = if !@@handler_options[:project].blank?
- @@handler_options[:project]
- elsif email.plain_text_body =~ %r{^Project:[ \t]*(.+)$}i
- $1
- end
-
- target = Project.find_by_identifier(identifier.to_s)
+ target = Project.find_by_identifier(get_keyword(:project))
raise MissingInformation.new('Unable to determine target project') if target.nil?
target
end
@@ -120,6 +123,14 @@ class MailHandler < ActionMailer::Base
end
end
end
+
+ def get_keyword(attr)
+ if @@handler_options[:allow_override].include?(attr.to_s) && email.plain_text_body =~ /^#{attr}:[ \t]*(.+)$/i
+ $1.strip
+ elsif !@@handler_options[:issue][attr].blank?
+ @@handler_options[:issue][attr]
+ end
+ end
end
class TMail::Mail
diff --git a/extra/mail_handler/rdm-mailhandler.rb b/extra/mail_handler/rdm-mailhandler.rb
index 585afefef..87a6798fa 100644
--- a/extra/mail_handler/rdm-mailhandler.rb
+++ b/extra/mail_handler/rdm-mailhandler.rb
@@ -12,16 +12,22 @@ require 'getoptlong'
class RedmineMailHandler
VERSION = '0.1'
- attr_accessor :verbose, :project, :url, :key
+ attr_accessor :verbose, :issue_attributes, :allow_override, :url, :key
def initialize
+ self.issue_attributes = {}
+
opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--version', '-V', GetoptLong::NO_ARGUMENT ],
[ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
[ '--url', '-u', GetoptLong::REQUIRED_ARGUMENT ],
[ '--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
- [ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ]
+ [ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--tracker', '-t', GetoptLong::REQUIRED_ARGUMENT],
+ [ '--category', GetoptLong::REQUIRED_ARGUMENT],
+ [ '--priority', GetoptLong::REQUIRED_ARGUMENT],
+ [ '--allow-override', '-o', GetoptLong::REQUIRED_ARGUMENT]
)
opts.each do |opt, arg|
@@ -36,8 +42,10 @@ class RedmineMailHandler
self.verbose = true
when '--version'
puts VERSION; exit
- when '--project'
- self.project = arg.dup
+ when '--project', '--tracker', '--category', '--priority'
+ self.issue_attributes[opt.gsub(%r{^\-\-}, '')] = arg.dup
+ when '--allow-override'
+ self.allow_override = arg.dup
end
end
@@ -46,8 +54,11 @@ class RedmineMailHandler
def submit(email)
uri = url.gsub(%r{/*$}, '') + '/mail_handler'
+
+ data = { 'key' => key, 'email' => email, 'allow_override' => allow_override }
+ issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }
+
debug "Posting to #{uri}..."
- data = { 'key' => key, 'project' => project, 'email' => email }
response = Net::HTTP.post_form(URI.parse(uri), data)
debug "Response received: #{response.code}"
response.code == 201 ? 0 : 1
@@ -56,17 +67,39 @@ class RedmineMailHandler
private
def usage
- puts "Usage: rdm-mailhandler [options] --url=<Redmine URL> --key=<API key>"
- puts "Reads an email from standard input and forward it to a Redmine server"
- puts
- puts "Options:"
- puts " --help show this help"
- puts " --verbose show extra information"
- puts " --project identifier of the target project"
- puts
- puts "Examples:"
- puts " rdm-mailhandler --url http://redmine.domain.foo --key secret"
- puts " rdm-mailhandler --url https://redmine.domain.foo --key secret --project foo"
+ puts <<-USAGE
+Usage: rdm-mailhandler [options] --url=<Redmine URL> --key=<API key>
+Reads an email from standard input and forward it to a Redmine server
+
+Required:
+ -u, --url URL of the Redmine server
+ -k, --key Redmine API key
+
+General options:
+ -h, --help show this help
+ -v, --verbose show extra information
+ -V, --version show version information and exit
+
+Issue attributes control options:
+ -p, --project=PROJECT identifier of the target project
+ -t, --tracker=TRACKER name of the target tracker
+ --category=CATEGORY name of the target category
+ --priority=PRIORITY name of the target priority
+ -o, --allow-override=ATTRS allow email content to override attributes
+ specified by previous options
+ ATTRS is a comma separated list of attributes
+
+Examples:
+ # No project specified. Emails MUST contain the 'Project' keyword:
+ rdm-mailhandler --url http://redmine.domain.foo --key secret
+
+ # Fixed project and default tracker specified, but emails can override
+ # both tracker and priority attributes:
+ rdm-mailhandler --url https://domain.foo/redmine --key secret \\
+ --project foo \\
+ --tracker bug \\
+ --allow-override tracker,priority
+USAGE
exit
end
diff --git a/lib/tasks/email.rake b/lib/tasks/email.rake
index 01407c36d..daf0aa9bb 100644
--- a/lib/tasks/email.rake
+++ b/lib/tasks/email.rake
@@ -21,16 +21,31 @@ namespace :redmine do
desc <<-END_DESC
Read an email from standard input.
-Available options:
- * project => identifier of the project the issue should be added to
-
-Example:
- rake redmine:email:receive project=foo RAILS_ENV="production"
+Issue attributes control options:
+ project=PROJECT identifier of the target project
+ tracker=TRACKER name of the target tracker
+ category=CATEGORY name of the target category
+ priority=PRIORITY name of the target priority
+ allow_override=ATTRS allow email content to override attributes
+ specified by previous options
+ ATTRS is a comma separated list of attributes
+
+Examples:
+ # No project specified. Emails MUST contain the 'Project' keyword:
+ rake redmine:email:read RAILS_ENV="production" < raw_email
+
+ # Fixed project and default tracker specified, but emails can override
+ # both tracker and priority attributes:
+ rake redmine:email:read RAILS_ENV="production" \\
+ project=foo \\
+ tracker=bug \\
+ allow_override=tracker,priority < raw_email
END_DESC
- task :receive => :environment do
- options = {}
- options[:project] = ENV['project'] if ENV['project']
+ task :read => :environment do
+ options = { :issue => {} }
+ %w(project tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
+ options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
MailHandler.receive(STDIN.read, options)
end
@@ -39,17 +54,37 @@ END_DESC
Read emails from an IMAP server.
Available IMAP options:
- * host => IMAP server host (default: 127.0.0.1)
- * port => IMAP server port (default: 143)
- * ssl => Use SSL? (default: false)
- * username => IMAP account
- * password => IMAP password
- * folder => IMAP folder to read (default: INBOX)
-Other options:
- * project => identifier of the project the issue should be added to
+ host=HOST IMAP server host (default: 127.0.0.1)
+ port=PORT IMAP server port (default: 143)
+ ssl=SSL Use SSL? (default: false)
+ username=USERNAME IMAP account
+ password=PASSWORD IMAP password
+ folder=FOLDER IMAP folder to read (default: INBOX)
+
+Issue attributes control options:
+ project=PROJECT identifier of the target project
+ tracker=TRACKER name of the target tracker
+ category=CATEGORY name of the target category
+ priority=PRIORITY name of the target priority
+ allow_override=ATTRS allow email content to override attributes
+ specified by previous options
+ ATTRS is a comma separated list of attributes
+
+Examples:
+ # No project specified. Emails MUST contain the 'Project' keyword:
+
+ rake redmine:email:receive_iamp RAILS_ENV="production" \\
+ host=imap.foo.bar username=redmine@somenet.foo password=xxx
+
+
+ # Fixed project and default tracker specified, but emails can override
+ # both tracker and priority attributes:
-Example:
- rake redmine:email:receive_iamp host=imap.foo.bar username=redmine@somenet.foo password=xxx project=foo RAILS_ENV="production"
+ rake redmine:email:receive_iamp RAILS_ENV="production" \\
+ host=imap.foo.bar username=redmine@somenet.foo password=xxx ssl=1 \\
+ project=foo \\
+ tracker=bug \\
+ allow_override=tracker,priority
END_DESC
task :receive_imap => :environment do
@@ -60,8 +95,9 @@ END_DESC
:password => ENV['password'],
:folder => ENV['folder']}
- options = {}
- options[:project] = ENV['project'] if ENV['project']
+ options = { :issue => {} }
+ %w(project tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
+ options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
Redmine::IMAP.check(imap_options, options)
end
diff --git a/test/fixtures/issue_categories.yml b/test/fixtures/issue_categories.yml
index 6c2a07b58..2b74b5977 100644
--- a/test/fixtures/issue_categories.yml
+++ b/test/fixtures/issue_categories.yml
@@ -9,3 +9,9 @@ issue_categories_002:
project_id: 1
assigned_to_id:
id: 2
+issue_categories_003:
+ name: Stock management
+ project_id: 2
+ assigned_to_id:
+ id: 3
+ \ No newline at end of file
diff --git a/test/fixtures/mail_handler/ticket_with_attributes.eml b/test/fixtures/mail_handler/ticket_with_attributes.eml
new file mode 100644
index 000000000..118523496
--- /dev/null
+++ b/test/fixtures/mail_handler/ticket_with_attributes.eml
@@ -0,0 +1,43 @@
+Return-Path: <jsmith@somenet.foo>
+Received: from osiris ([127.0.0.1])
+ by OSIRIS
+ with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
+Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
+From: "John Smith" <jsmith@somenet.foo>
+To: <redmine@somenet.foo>
+Subject: New ticket on a given project
+Date: Sun, 22 Jun 2008 12:28:07 +0200
+MIME-Version: 1.0
+Content-Type: text/plain;
+ format=flowed;
+ charset="iso-8859-1";
+ reply-type=original
+Content-Transfer-Encoding: 7bit
+X-Priority: 3
+X-MSMail-Priority: Normal
+X-Mailer: Microsoft Outlook Express 6.00.2900.2869
+X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
+
+Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
+turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
+blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
+sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
+in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
+sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
+id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
+eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
+sed, mauris. Pellentesque habitant morbi tristique senectus et netus et
+malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
+platea dictumst.
+
+Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
+sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
+Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
+dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
+massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
+pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
+
+Project: onlinestore
+Tracker: Feature request
+category: Stock management
+priority: Urgent
diff --git a/test/unit/mail_handler_test.rb b/test/unit/mail_handler_test.rb
index 6bb638f21..6e8a47c35 100644
--- a/test/unit/mail_handler_test.rb
+++ b/test/unit/mail_handler_test.rb
@@ -18,7 +18,15 @@
require File.dirname(__FILE__) + '/../test_helper'
class MailHandlerTest < Test::Unit::TestCase
- fixtures :users, :projects, :enabled_modules, :roles, :members, :issues, :trackers, :enumerations
+ fixtures :users, :projects,
+ :enabled_modules,
+ :roles,
+ :members,
+ :issues,
+ :trackers,
+ :projects_trackers,
+ :enumerations,
+ :issue_categories
FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
@@ -38,8 +46,36 @@ class MailHandlerTest < Test::Unit::TestCase
assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
end
+ def test_add_issue_with_attributes_override
+ issue = submit_email('ticket_with_attributes.eml', :allow_override => 'tracker,category,priority')
+ assert issue.is_a?(Issue)
+ assert !issue.new_record?
+ issue.reload
+ assert_equal 'New ticket on a given project', issue.subject
+ assert_equal User.find_by_login('jsmith'), issue.author
+ assert_equal Project.find(2), issue.project
+ assert_equal 'Feature request', issue.tracker.to_s
+ assert_equal 'Stock management', issue.category.to_s
+ assert_equal 'Urgent', issue.priority.to_s
+ assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
+ end
+
+ def test_add_issue_with_partial_attributes_override
+ issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker'])
+ assert issue.is_a?(Issue)
+ assert !issue.new_record?
+ issue.reload
+ assert_equal 'New ticket on a given project', issue.subject
+ assert_equal User.find_by_login('jsmith'), issue.author
+ assert_equal Project.find(2), issue.project
+ assert_equal 'Feature request', issue.tracker.to_s
+ assert_nil issue.category
+ assert_equal 'High', issue.priority.to_s
+ assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
+ end
+
def test_add_issue_with_attachment_to_specific_project
- issue = submit_email('ticket_with_attachment.eml', :project => 'onlinestore')
+ issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
assert issue.is_a?(Issue)
assert !issue.new_record?
issue.reload