@@ -0,0 +1,43 | |||
|
1 | Return-Path: <jsmith@somenet.foo> | |
|
2 | Received: from osiris ([127.0.0.1]) | |
|
3 | by OSIRIS | |
|
4 | with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200 | |
|
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: Sun, 22 Jun 2008 12:28:07 +0200 | |
|
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 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet | |
|
22 | turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus | |
|
23 | blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti | |
|
24 | sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In | |
|
25 | in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras | |
|
26 | sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum | |
|
27 | id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus | |
|
28 | eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique | |
|
29 | sed, mauris. Pellentesque habitant morbi tristique senectus et netus et | |
|
30 | malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse | |
|
31 | platea dictumst. | |
|
32 | ||
|
33 | Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque | |
|
34 | sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem. | |
|
35 | Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et, | |
|
36 | dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed, | |
|
37 | massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo | |
|
38 | pulvinar dui, a gravida orci mi eget odio. Nunc a lacus. | |
|
39 | ||
|
40 | Project: onlinestore | |
|
41 | Tracker: Feature request | |
|
42 | category: Stock management | |
|
43 | priority: Urgent |
@@ -1,134 +1,145 | |||
|
1 | 1 | # redMine - project management software |
|
2 | 2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
3 | 3 | # |
|
4 | 4 | # This program is free software; you can redistribute it and/or |
|
5 | 5 | # modify it under the terms of the GNU General Public License |
|
6 | 6 | # as published by the Free Software Foundation; either version 2 |
|
7 | 7 | # of the License, or (at your option) any later version. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU General Public License |
|
15 | 15 | # along with this program; if not, write to the Free Software |
|
16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | 17 | |
|
18 | 18 | class MailHandler < ActionMailer::Base |
|
19 | 19 | |
|
20 | 20 | class UnauthorizedAction < StandardError; end |
|
21 | 21 | class MissingInformation < StandardError; end |
|
22 | 22 | |
|
23 | 23 | attr_reader :email, :user |
|
24 | 24 | |
|
25 | 25 | def self.receive(email, options={}) |
|
26 | @@handler_options = options | |
|
26 | @@handler_options = options.dup | |
|
27 | ||
|
28 | @@handler_options[:issue] ||= {} | |
|
29 | ||
|
30 | @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) if @@handler_options[:allow_override].is_a?(String) | |
|
31 | @@handler_options[:allow_override] ||= [] | |
|
32 | # Project needs to be overridable if not specified | |
|
33 | @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project) | |
|
27 | 34 | super email |
|
28 | 35 | end |
|
29 | 36 | |
|
30 | 37 | # Processes incoming emails |
|
31 | 38 | def receive(email) |
|
32 | 39 | @email = email |
|
33 | 40 | @user = User.find_active(:first, :conditions => {:mail => email.from.first}) |
|
34 | 41 | unless @user |
|
35 | 42 | # Unknown user => the email is ignored |
|
36 | 43 | # TODO: ability to create the user's account |
|
37 | 44 | logger.info "MailHandler: email submitted by unknown user [#{email.from.first}]" if logger && logger.info |
|
38 | 45 | return false |
|
39 | 46 | end |
|
40 | 47 | User.current = @user |
|
41 | 48 | dispatch |
|
42 | 49 | end |
|
43 | 50 | |
|
44 | 51 | private |
|
45 | 52 | |
|
46 | 53 | ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]+#(\d+)\]} |
|
47 | 54 | |
|
48 | 55 | def dispatch |
|
49 | 56 | if m = email.subject.match(ISSUE_REPLY_SUBJECT_RE) |
|
50 | 57 | receive_issue_update(m[1].to_i) |
|
51 | 58 | else |
|
52 | 59 | receive_issue |
|
53 | 60 | end |
|
54 | 61 | rescue ActiveRecord::RecordInvalid => e |
|
55 | 62 | # TODO: send a email to the user |
|
56 | 63 | logger.error e.message if logger |
|
57 | 64 | false |
|
58 | 65 | rescue MissingInformation => e |
|
59 | 66 | logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger |
|
60 | 67 | false |
|
61 | 68 | rescue UnauthorizedAction => e |
|
62 | 69 | logger.error "MailHandler: unauthorized attempt from #{user}" if logger |
|
63 | 70 | false |
|
64 | 71 | end |
|
65 | 72 | |
|
66 | 73 | # Creates a new issue |
|
67 | 74 | def receive_issue |
|
68 | 75 | project = target_project |
|
69 | # TODO: make the tracker configurable | |
|
70 | tracker = project.trackers.find(:first) | |
|
76 | tracker = (get_keyword(:tracker) && project.trackers.find_by_name(get_keyword(:tracker))) || project.trackers.find(:first) | |
|
77 | category = (get_keyword(:category) && project.issue_categories.find_by_name(get_keyword(:category))) | |
|
78 | priority = (get_keyword(:priority) && Enumeration.find_by_opt_and_name('IPRI', get_keyword(:priority))) | |
|
79 | ||
|
71 | 80 | # check permission |
|
72 | 81 | raise UnauthorizedAction unless user.allowed_to?(:add_issues, project) |
|
73 | issue = Issue.new(:author => user, :project => project, :tracker => tracker) | |
|
82 | issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority) | |
|
74 | 83 | issue.subject = email.subject.chomp |
|
75 | 84 | issue.description = email.plain_text_body.chomp |
|
76 | 85 | issue.save! |
|
77 | 86 | add_attachments(issue) |
|
78 | 87 | logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info |
|
79 | 88 | Mailer.deliver_issue_add(issue) if Setting.notified_events.include?('issue_added') |
|
80 | 89 | issue |
|
81 | 90 | end |
|
82 | 91 | |
|
83 | 92 | def target_project |
|
84 | 93 | # TODO: other ways to specify project: |
|
85 | 94 | # * parse the email To field |
|
86 | 95 | # * specific project (eg. Setting.mail_handler_target_project) |
|
87 | identifier = if !@@handler_options[:project].blank? | |
|
88 | @@handler_options[:project] | |
|
89 | elsif email.plain_text_body =~ %r{^Project:[ \t]*(.+)$}i | |
|
90 | $1 | |
|
91 | end | |
|
92 | ||
|
93 | target = Project.find_by_identifier(identifier.to_s) | |
|
96 | target = Project.find_by_identifier(get_keyword(:project)) | |
|
94 | 97 | raise MissingInformation.new('Unable to determine target project') if target.nil? |
|
95 | 98 | target |
|
96 | 99 | end |
|
97 | 100 | |
|
98 | 101 | # Adds a note to an existing issue |
|
99 | 102 | def receive_issue_update(issue_id) |
|
100 | 103 | issue = Issue.find_by_id(issue_id) |
|
101 | 104 | return unless issue |
|
102 | 105 | # check permission |
|
103 | 106 | raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project) |
|
104 | 107 | # add the note |
|
105 | 108 | journal = issue.init_journal(user, email.plain_text_body.chomp) |
|
106 | 109 | add_attachments(issue) |
|
107 | 110 | issue.save! |
|
108 | 111 | logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info |
|
109 | 112 | Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated') |
|
110 | 113 | journal |
|
111 | 114 | end |
|
112 | 115 | |
|
113 | 116 | def add_attachments(obj) |
|
114 | 117 | if email.has_attachments? |
|
115 | 118 | email.attachments.each do |attachment| |
|
116 | 119 | Attachment.create(:container => obj, |
|
117 | 120 | :file => attachment, |
|
118 | 121 | :author => user, |
|
119 | 122 | :content_type => attachment.content_type) |
|
120 | 123 | end |
|
121 | 124 | end |
|
122 | 125 | end |
|
126 | ||
|
127 | def get_keyword(attr) | |
|
128 | if @@handler_options[:allow_override].include?(attr.to_s) && email.plain_text_body =~ /^#{attr}:[ \t]*(.+)$/i | |
|
129 | $1.strip | |
|
130 | elsif !@@handler_options[:issue][attr].blank? | |
|
131 | @@handler_options[:issue][attr] | |
|
132 | end | |
|
133 | end | |
|
123 | 134 | end |
|
124 | 135 | |
|
125 | 136 | class TMail::Mail |
|
126 | 137 | # Returns body of the first plain text part found if any |
|
127 | 138 | def plain_text_body |
|
128 | 139 | return @plain_text_body unless @plain_text_body.nil? |
|
129 | 140 | p = self.parts.collect {|c| (c.respond_to?(:parts) && !c.parts.empty?) ? c.parts : c}.flatten |
|
130 | 141 | plain = p.detect {|c| c.content_type == 'text/plain'} |
|
131 | 142 | @plain_text_body = plain.nil? ? self.body : plain.body |
|
132 | 143 | end |
|
133 | 144 | end |
|
134 | 145 |
@@ -1,79 +1,112 | |||
|
1 | 1 | #!/usr/bin/ruby |
|
2 | 2 | |
|
3 | 3 | # rdm-mailhandler |
|
4 | 4 | # Reads an email from standard input and forward it to a Redmine server |
|
5 | 5 | # Can be used from a remote mail server |
|
6 | 6 | |
|
7 | 7 | require 'net/http' |
|
8 | 8 | require 'net/https' |
|
9 | 9 | require 'uri' |
|
10 | 10 | require 'getoptlong' |
|
11 | 11 | |
|
12 | 12 | class RedmineMailHandler |
|
13 | 13 | VERSION = '0.1' |
|
14 | 14 | |
|
15 |
attr_accessor :verbose, : |
|
|
15 | attr_accessor :verbose, :issue_attributes, :allow_override, :url, :key | |
|
16 | 16 | |
|
17 | 17 | def initialize |
|
18 | self.issue_attributes = {} | |
|
19 | ||
|
18 | 20 | opts = GetoptLong.new( |
|
19 | 21 | [ '--help', '-h', GetoptLong::NO_ARGUMENT ], |
|
20 | 22 | [ '--version', '-V', GetoptLong::NO_ARGUMENT ], |
|
21 | 23 | [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ], |
|
22 | 24 | [ '--url', '-u', GetoptLong::REQUIRED_ARGUMENT ], |
|
23 | 25 | [ '--key', '-k', GetoptLong::REQUIRED_ARGUMENT], |
|
24 | [ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ] | |
|
26 | [ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ], | |
|
27 | [ '--tracker', '-t', GetoptLong::REQUIRED_ARGUMENT], | |
|
28 | [ '--category', GetoptLong::REQUIRED_ARGUMENT], | |
|
29 | [ '--priority', GetoptLong::REQUIRED_ARGUMENT], | |
|
30 | [ '--allow-override', '-o', GetoptLong::REQUIRED_ARGUMENT] | |
|
25 | 31 | ) |
|
26 | 32 | |
|
27 | 33 | opts.each do |opt, arg| |
|
28 | 34 | case opt |
|
29 | 35 | when '--url' |
|
30 | 36 | self.url = arg.dup |
|
31 | 37 | when '--key' |
|
32 | 38 | self.key = arg.dup |
|
33 | 39 | when '--help' |
|
34 | 40 | usage |
|
35 | 41 | when '--verbose' |
|
36 | 42 | self.verbose = true |
|
37 | 43 | when '--version' |
|
38 | 44 | puts VERSION; exit |
|
39 | when '--project' | |
|
40 | self.project = arg.dup | |
|
45 | when '--project', '--tracker', '--category', '--priority' | |
|
46 | self.issue_attributes[opt.gsub(%r{^\-\-}, '')] = arg.dup | |
|
47 | when '--allow-override' | |
|
48 | self.allow_override = arg.dup | |
|
41 | 49 | end |
|
42 | 50 | end |
|
43 | 51 | |
|
44 | 52 | usage if url.nil? |
|
45 | 53 | end |
|
46 | 54 | |
|
47 | 55 | def submit(email) |
|
48 | 56 | uri = url.gsub(%r{/*$}, '') + '/mail_handler' |
|
57 | ||
|
58 | data = { 'key' => key, 'email' => email, 'allow_override' => allow_override } | |
|
59 | issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value } | |
|
60 | ||
|
49 | 61 | debug "Posting to #{uri}..." |
|
50 | data = { 'key' => key, 'project' => project, 'email' => email } | |
|
51 | 62 | response = Net::HTTP.post_form(URI.parse(uri), data) |
|
52 | 63 | debug "Response received: #{response.code}" |
|
53 | 64 | response.code == 201 ? 0 : 1 |
|
54 | 65 | end |
|
55 | 66 | |
|
56 | 67 | private |
|
57 | 68 | |
|
58 | 69 | def usage |
|
59 | puts "Usage: rdm-mailhandler [options] --url=<Redmine URL> --key=<API key>" | |
|
60 | puts "Reads an email from standard input and forward it to a Redmine server" | |
|
61 | puts | |
|
62 | puts "Options:" | |
|
63 | puts " --help show this help" | |
|
64 | puts " --verbose show extra information" | |
|
65 | puts " --project identifier of the target project" | |
|
66 | puts | |
|
67 | puts "Examples:" | |
|
68 | puts " rdm-mailhandler --url http://redmine.domain.foo --key secret" | |
|
69 | puts " rdm-mailhandler --url https://redmine.domain.foo --key secret --project foo" | |
|
70 | puts <<-USAGE | |
|
71 | Usage: rdm-mailhandler [options] --url=<Redmine URL> --key=<API key> | |
|
72 | Reads an email from standard input and forward it to a Redmine server | |
|
73 | ||
|
74 | Required: | |
|
75 | -u, --url URL of the Redmine server | |
|
76 | -k, --key Redmine API key | |
|
77 | ||
|
78 | General options: | |
|
79 | -h, --help show this help | |
|
80 | -v, --verbose show extra information | |
|
81 | -V, --version show version information and exit | |
|
82 | ||
|
83 | Issue attributes control options: | |
|
84 | -p, --project=PROJECT identifier of the target project | |
|
85 | -t, --tracker=TRACKER name of the target tracker | |
|
86 | --category=CATEGORY name of the target category | |
|
87 | --priority=PRIORITY name of the target priority | |
|
88 | -o, --allow-override=ATTRS allow email content to override attributes | |
|
89 | specified by previous options | |
|
90 | ATTRS is a comma separated list of attributes | |
|
91 | ||
|
92 | Examples: | |
|
93 | # No project specified. Emails MUST contain the 'Project' keyword: | |
|
94 | rdm-mailhandler --url http://redmine.domain.foo --key secret | |
|
95 | ||
|
96 | # Fixed project and default tracker specified, but emails can override | |
|
97 | # both tracker and priority attributes: | |
|
98 | rdm-mailhandler --url https://domain.foo/redmine --key secret \\ | |
|
99 | --project foo \\ | |
|
100 | --tracker bug \\ | |
|
101 | --allow-override tracker,priority | |
|
102 | USAGE | |
|
70 | 103 | exit |
|
71 | 104 | end |
|
72 | 105 | |
|
73 | 106 | def debug(msg) |
|
74 | 107 | puts msg if verbose |
|
75 | 108 | end |
|
76 | 109 | end |
|
77 | 110 | |
|
78 | 111 | handler = RedmineMailHandler.new |
|
79 | 112 | handler.submit(STDIN.read) |
@@ -1,69 +1,105 | |||
|
1 | 1 | # redMine - project management software |
|
2 | 2 | # Copyright (C) 2006-2008 Jean-Philippe Lang |
|
3 | 3 | # |
|
4 | 4 | # This program is free software; you can redistribute it and/or |
|
5 | 5 | # modify it under the terms of the GNU General Public License |
|
6 | 6 | # as published by the Free Software Foundation; either version 2 |
|
7 | 7 | # of the License, or (at your option) any later version. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU General Public License |
|
15 | 15 | # along with this program; if not, write to the Free Software |
|
16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | 17 | |
|
18 | 18 | namespace :redmine do |
|
19 | 19 | namespace :email do |
|
20 | 20 | |
|
21 | 21 | desc <<-END_DESC |
|
22 | 22 | Read an email from standard input. |
|
23 | 23 | |
|
24 | Available options: | |
|
25 | * project => identifier of the project the issue should be added to | |
|
24 | Issue attributes control options: | |
|
25 | project=PROJECT identifier of the target project | |
|
26 | tracker=TRACKER name of the target tracker | |
|
27 | category=CATEGORY name of the target category | |
|
28 | priority=PRIORITY name of the target priority | |
|
29 | allow_override=ATTRS allow email content to override attributes | |
|
30 | specified by previous options | |
|
31 | ATTRS is a comma separated list of attributes | |
|
26 | 32 | |
|
27 | Example: | |
|
28 | rake redmine:email:receive project=foo RAILS_ENV="production" | |
|
33 | Examples: | |
|
34 | # No project specified. Emails MUST contain the 'Project' keyword: | |
|
35 | rake redmine:email:read RAILS_ENV="production" < raw_email | |
|
36 | ||
|
37 | # Fixed project and default tracker specified, but emails can override | |
|
38 | # both tracker and priority attributes: | |
|
39 | rake redmine:email:read RAILS_ENV="production" \\ | |
|
40 | project=foo \\ | |
|
41 | tracker=bug \\ | |
|
42 | allow_override=tracker,priority < raw_email | |
|
29 | 43 | END_DESC |
|
30 | 44 | |
|
31 |
task :re |
|
|
32 | options = {} | |
|
33 | options[:project] = ENV['project'] if ENV['project'] | |
|
45 | task :read => :environment do | |
|
46 | options = { :issue => {} } | |
|
47 | %w(project tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] } | |
|
48 | options[:allow_override] = ENV['allow_override'] if ENV['allow_override'] | |
|
34 | 49 | |
|
35 | 50 | MailHandler.receive(STDIN.read, options) |
|
36 | 51 | end |
|
37 | 52 | |
|
38 | 53 | desc <<-END_DESC |
|
39 | 54 | Read emails from an IMAP server. |
|
40 | 55 | |
|
41 | 56 | Available IMAP options: |
|
42 |
|
|
|
43 |
|
|
|
44 |
|
|
|
45 |
|
|
|
46 |
|
|
|
47 |
|
|
|
48 | Other options: | |
|
49 | * project => identifier of the project the issue should be added to | |
|
50 | ||
|
51 | Example: | |
|
52 | rake redmine:email:receive_iamp host=imap.foo.bar username=redmine@somenet.foo password=xxx project=foo RAILS_ENV="production" | |
|
57 | host=HOST IMAP server host (default: 127.0.0.1) | |
|
58 | port=PORT IMAP server port (default: 143) | |
|
59 | ssl=SSL Use SSL? (default: false) | |
|
60 | username=USERNAME IMAP account | |
|
61 | password=PASSWORD IMAP password | |
|
62 | folder=FOLDER IMAP folder to read (default: INBOX) | |
|
63 | ||
|
64 | Issue attributes control options: | |
|
65 | project=PROJECT identifier of the target project | |
|
66 | tracker=TRACKER name of the target tracker | |
|
67 | category=CATEGORY name of the target category | |
|
68 | priority=PRIORITY name of the target priority | |
|
69 | allow_override=ATTRS allow email content to override attributes | |
|
70 | specified by previous options | |
|
71 | ATTRS is a comma separated list of attributes | |
|
72 | ||
|
73 | Examples: | |
|
74 | # No project specified. Emails MUST contain the 'Project' keyword: | |
|
75 | ||
|
76 | rake redmine:email:receive_iamp RAILS_ENV="production" \\ | |
|
77 | host=imap.foo.bar username=redmine@somenet.foo password=xxx | |
|
78 | ||
|
79 | ||
|
80 | # Fixed project and default tracker specified, but emails can override | |
|
81 | # both tracker and priority attributes: | |
|
82 | ||
|
83 | rake redmine:email:receive_iamp RAILS_ENV="production" \\ | |
|
84 | host=imap.foo.bar username=redmine@somenet.foo password=xxx ssl=1 \\ | |
|
85 | project=foo \\ | |
|
86 | tracker=bug \\ | |
|
87 | allow_override=tracker,priority | |
|
53 | 88 | END_DESC |
|
54 | 89 | |
|
55 | 90 | task :receive_imap => :environment do |
|
56 | 91 | imap_options = {:host => ENV['host'], |
|
57 | 92 | :port => ENV['port'], |
|
58 | 93 | :ssl => ENV['ssl'], |
|
59 | 94 | :username => ENV['username'], |
|
60 | 95 | :password => ENV['password'], |
|
61 | 96 | :folder => ENV['folder']} |
|
62 | 97 | |
|
63 | options = {} | |
|
64 | options[:project] = ENV['project'] if ENV['project'] | |
|
98 | options = { :issue => {} } | |
|
99 | %w(project tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] } | |
|
100 | options[:allow_override] = ENV['allow_override'] if ENV['allow_override'] | |
|
65 | 101 | |
|
66 | 102 | Redmine::IMAP.check(imap_options, options) |
|
67 | 103 | end |
|
68 | 104 | end |
|
69 | 105 | end |
@@ -1,11 +1,17 | |||
|
1 | 1 | --- |
|
2 | 2 | issue_categories_001: |
|
3 | 3 | name: Printing |
|
4 | 4 | project_id: 1 |
|
5 | 5 | assigned_to_id: 2 |
|
6 | 6 | id: 1 |
|
7 | 7 | issue_categories_002: |
|
8 | 8 | name: Recipes |
|
9 | 9 | project_id: 1 |
|
10 | 10 | assigned_to_id: |
|
11 | 11 | id: 2 |
|
12 | issue_categories_003: | |
|
13 | name: Stock management | |
|
14 | project_id: 2 | |
|
15 | assigned_to_id: | |
|
16 | id: 3 | |
|
17 | No newline at end of file |
@@ -1,71 +1,107 | |||
|
1 | 1 | # redMine - project management software |
|
2 | 2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
3 | 3 | # |
|
4 | 4 | # This program is free software; you can redistribute it and/or |
|
5 | 5 | # modify it under the terms of the GNU General Public License |
|
6 | 6 | # as published by the Free Software Foundation; either version 2 |
|
7 | 7 | # of the License, or (at your option) any later version. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU General Public License |
|
15 | 15 | # along with this program; if not, write to the Free Software |
|
16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | 17 | |
|
18 | 18 | require File.dirname(__FILE__) + '/../test_helper' |
|
19 | 19 | |
|
20 | 20 | class MailHandlerTest < Test::Unit::TestCase |
|
21 | fixtures :users, :projects, :enabled_modules, :roles, :members, :issues, :trackers, :enumerations | |
|
21 | fixtures :users, :projects, | |
|
22 | :enabled_modules, | |
|
23 | :roles, | |
|
24 | :members, | |
|
25 | :issues, | |
|
26 | :trackers, | |
|
27 | :projects_trackers, | |
|
28 | :enumerations, | |
|
29 | :issue_categories | |
|
22 | 30 | |
|
23 | 31 | FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' |
|
24 | 32 | |
|
25 | 33 | def setup |
|
26 | 34 | ActionMailer::Base.deliveries.clear |
|
27 | 35 | end |
|
28 | 36 | |
|
29 | 37 | def test_add_issue |
|
30 | 38 | # This email contains: 'Project: onlinestore' |
|
31 | 39 | issue = submit_email('ticket_on_given_project.eml') |
|
32 | 40 | assert issue.is_a?(Issue) |
|
33 | 41 | assert !issue.new_record? |
|
34 | 42 | issue.reload |
|
35 | 43 | assert_equal 'New ticket on a given project', issue.subject |
|
36 | 44 | assert_equal User.find_by_login('jsmith'), issue.author |
|
37 | 45 | assert_equal Project.find(2), issue.project |
|
38 | 46 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
39 | 47 | end |
|
40 | 48 | |
|
49 | def test_add_issue_with_attributes_override | |
|
50 | issue = submit_email('ticket_with_attributes.eml', :allow_override => 'tracker,category,priority') | |
|
51 | assert issue.is_a?(Issue) | |
|
52 | assert !issue.new_record? | |
|
53 | issue.reload | |
|
54 | assert_equal 'New ticket on a given project', issue.subject | |
|
55 | assert_equal User.find_by_login('jsmith'), issue.author | |
|
56 | assert_equal Project.find(2), issue.project | |
|
57 | assert_equal 'Feature request', issue.tracker.to_s | |
|
58 | assert_equal 'Stock management', issue.category.to_s | |
|
59 | assert_equal 'Urgent', issue.priority.to_s | |
|
60 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') | |
|
61 | end | |
|
62 | ||
|
63 | def test_add_issue_with_partial_attributes_override | |
|
64 | issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker']) | |
|
65 | assert issue.is_a?(Issue) | |
|
66 | assert !issue.new_record? | |
|
67 | issue.reload | |
|
68 | assert_equal 'New ticket on a given project', issue.subject | |
|
69 | assert_equal User.find_by_login('jsmith'), issue.author | |
|
70 | assert_equal Project.find(2), issue.project | |
|
71 | assert_equal 'Feature request', issue.tracker.to_s | |
|
72 | assert_nil issue.category | |
|
73 | assert_equal 'High', issue.priority.to_s | |
|
74 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') | |
|
75 | end | |
|
76 | ||
|
41 | 77 | def test_add_issue_with_attachment_to_specific_project |
|
42 | issue = submit_email('ticket_with_attachment.eml', :project => 'onlinestore') | |
|
78 | issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'}) | |
|
43 | 79 | assert issue.is_a?(Issue) |
|
44 | 80 | assert !issue.new_record? |
|
45 | 81 | issue.reload |
|
46 | 82 | assert_equal 'Ticket created by email with attachment', issue.subject |
|
47 | 83 | assert_equal User.find_by_login('jsmith'), issue.author |
|
48 | 84 | assert_equal Project.find(2), issue.project |
|
49 | 85 | assert_equal 'This is a new ticket with attachments', issue.description |
|
50 | 86 | # Attachment properties |
|
51 | 87 | assert_equal 1, issue.attachments.size |
|
52 | 88 | assert_equal 'Paella.jpg', issue.attachments.first.filename |
|
53 | 89 | assert_equal 'image/jpeg', issue.attachments.first.content_type |
|
54 | 90 | assert_equal 10790, issue.attachments.first.filesize |
|
55 | 91 | end |
|
56 | 92 | |
|
57 | 93 | def test_add_issue_note |
|
58 | 94 | journal = submit_email('ticket_reply.eml') |
|
59 | 95 | assert journal.is_a?(Journal) |
|
60 | 96 | assert_equal User.find_by_login('jsmith'), journal.user |
|
61 | 97 | assert_equal Issue.find(2), journal.journalized |
|
62 | 98 | assert_equal 'This is reply', journal.notes |
|
63 | 99 | end |
|
64 | 100 | |
|
65 | 101 | private |
|
66 | 102 | |
|
67 | 103 | def submit_email(filename, options={}) |
|
68 | 104 | raw = IO.read(File.join(FIXTURES_PATH, filename)) |
|
69 | 105 | MailHandler.receive(raw, options) |
|
70 | 106 | end |
|
71 | 107 | end |
General Comments 0
You need to be logged in to leave comments.
Login now