Index: test/unit/mail_handler_test.rb =================================================================== --- test/unit/mail_handler_test.rb (revision 1648) +++ test/unit/mail_handler_test.rb (working copy) @@ -26,7 +26,8 @@ :trackers, :projects_trackers, :enumerations, - :issue_categories + :issue_categories, + :boards FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' @@ -97,7 +98,56 @@ assert_equal Issue.find(2), journal.journalized assert_equal 'This is reply', journal.notes end + + def test_add_forum_message + # This email subject contains: [eCookbook - Discussion] A new message on a forum + message = submit_email('message_on_given_project.eml') + assert message.is_a?(Message) + assert !message.new_record? + message.reload + assert_equal 'A new message on a forum', message.subject + assert_equal User.find_by_login('jsmith'), message.author + assert_equal Board.find(2), message.board + assert message.content.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') + end + + def test_add_forum_message_reply + # This email subject contains: Re: [eCookbook - Help] First post + message = submit_email('message_reply.eml') + assert message.is_a?(Message) + assert !message.new_record? + message.reload + assert_equal 'Re: First post', message.subject + assert_equal User.find_by_login('jsmith'), message.author + assert_equal Board.find(1), message.board + assert_equal Message.find(1).id, message.parent_id + assert message.content.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') + end + + def test_reject_invalid_message_project + # This email subject contains: [Bad Project Name - Discussion] + message = submit_email('message_invalid_project.eml') + assert !message + end + + def test_reject_invalid_message_board + # This email subject contains: [eCookbook - Invalid Board Name] + message = submit_email('message_invalid_board.eml') + assert !message + end + + def test_reject_invalid_user_email + # This email is from: not_a_valid_user_email@somenet.foo + message = submit_email('message_invalid_user_email.eml') + assert !message + end + def test_reject_unsupported_email + # This email is from a valid user but has no content for an issue or message + message = submit_email('unsupported_email.eml') + assert !message + end + private def submit_email(filename, options={}) Index: app/models/mail_handler.rb =================================================================== --- app/models/mail_handler.rb (revision 1648) +++ app/models/mail_handler.rb (working copy) @@ -51,10 +51,19 @@ private ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]+#(\d+)\]} + FORUM_MESSAGE_SUBJECT = %r{(\[.*)(#.*\])} + FORUM_MESSAGE_SUBJECT_RE = %r{re: }i + FORUM_MESSAGE_SUBJECT_SPLIT = %r{(\[)(.*)( - )(.*)(\])} def dispatch if m = email.subject.match(ISSUE_REPLY_SUBJECT_RE) receive_issue_update(m[1].to_i) + elsif !email.subject.match(FORUM_MESSAGE_SUBJECT) && email.subject.match(/\[/) + if email.subject.match(FORUM_MESSAGE_SUBJECT_RE) + receive_forum_message_reply + else + receive_forum_message + end else receive_issue end @@ -89,15 +98,58 @@ issue end + # Creates a new forum message + def receive_forum_message + project = target_project + board = target_board + # check permission + raise UnauthorizedAction unless user.allowed_to?(:edit_messages, project) + message = Message.new(:author => user, :board => board) + message.subject = email.subject.split(FORUM_MESSAGE_SUBJECT_SPLIT)[6].strip + message.content = email.plain_text_body.chomp + message.save! + logger.info "MailHandler: message ##{message.id} - #{message.subject} created by #{user}" if logger && logger.info + #Mailer.deliver_message_posted(message) if Setting.notified_events.include?('message_posted') + message + end + + # Creates a reply to an existing forum message + def receive_forum_message_reply + project = target_project + board = target_board + # check permission + raise UnauthorizedAction unless user.allowed_to?(:edit_messages, project) + message = Message.new(:author => user, :board => board) + subject = email.subject.split(FORUM_MESSAGE_SUBJECT_SPLIT)[6].gsub(FORUM_MESSAGE_SUBJECT_RE, "").strip + message.parent_id = Message.find(:first, + :conditions => [ "board_id = ? AND subject = ?", + board, subject ], + :order => "created_on DESC").id + message.subject = "Re: " + subject + message.content = email.plain_text_body.chomp + message.save! + logger.info "MailHandler: message ##{message.id} - #{message.subject} created by #{user} in reply to message ##{message.parent_id}" if logger && logger.info + #Mailer.deliver_message_posted(message) if Setting.notified_events.include?('message_posted') + message + 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)) + unless target = Project.find_by_identifier(get_keyword(:project)) + target = Project.find_by_name(email.subject.split(FORUM_MESSAGE_SUBJECT_SPLIT)[2]) + end raise MissingInformation.new('Unable to determine target project') if target.nil? target end + def target_board + target = Board.find_by_name(email.subject.split(FORUM_MESSAGE_SUBJECT_SPLIT)[4].strip) + raise MissingInformation.new('Unable to determine target board/forum') if target.nil? + target + end + # Adds a note to an existing issue def receive_issue_update(issue_id) issue = Issue.find_by_id(issue_id) Index: app/models/mailer.rb =================================================================== --- app/models/mailer.rb (revision 1648) +++ app/models/mailer.rb (working copy) @@ -99,6 +99,7 @@ def message_posted(message, recipients) redmine_headers 'Project' => message.project.identifier, 'Topic-Id' => (message.parent_id || message.id) + from Setting.forum_mail_from unless Setting.forum_mail_from.empty? recipients(recipients) subject "[#{message.board.project.name} - #{message.board.name}] #{message.subject}" body :message => message, Index: app/views/settings/_notifications.rhtml =================================================================== --- app/views/settings/_notifications.rhtml (revision 1648) +++ app/views/settings/_notifications.rhtml (working copy) @@ -5,6 +5,9 @@
<%= text_field_tag 'settings[mail_from]', Setting.mail_from, :size => 60 %>
++<%= text_field_tag 'settings[forum_mail_from]', Setting.forum_mail_from, :size => 60 %>
+<%= check_box_tag 'settings[bcc_recipients]', 1, Setting.bcc_recipients? %> <%= hidden_field_tag 'settings[bcc_recipients]', 0 %>
Index: lang/en.yml =================================================================== --- lang/en.yml (revision 1648) +++ lang/en.yml (working copy) @@ -192,6 +192,7 @@ setting_attachment_max_size: Attachment max. size setting_issues_export_limit: Issues export limit setting_mail_from: Emission email address +setting_forum_mail_from: Forum emission email address setting_bcc_recipients: Blind carbon copy recipients (bcc) setting_host_name: Host name setting_text_formatting: Text formatting Index: config/settings.yml =================================================================== --- config/settings.yml (revision 1648) +++ config/settings.yml (working copy) @@ -44,6 +44,8 @@ default: '25,50,100' mail_from: default: redmine@somenet.foo +forum_mail_from: + default: "" bcc_recipients: default: 1 text_formatting: