Mercurial > hg > soundsoftware-site
diff app/models/mail_handler.rb @ 511:107d36338b70 live
Merge from branch "cannam"
author | Chris Cannam |
---|---|
date | Thu, 14 Jul 2011 10:43:07 +0100 |
parents | 851510f1b535 |
children | 5e80956cc792 |
line wrap: on
line diff
--- a/app/models/mail_handler.rb Thu Jun 09 16:51:06 2011 +0100 +++ b/app/models/mail_handler.rb Thu Jul 14 10:43:07 2011 +0100 @@ -1,16 +1,16 @@ -# redMine - project management software -# Copyright (C) 2006-2007 Jean-Philippe Lang +# Redmine - project management software +# Copyright (C) 2006-2011 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -21,25 +21,25 @@ class UnauthorizedAction < StandardError; end class MissingInformation < StandardError; end - + attr_reader :email, :user def self.receive(email, 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) # Status overridable by default - @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status) - + @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status) + @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1' ? true : false) super email end - + # Processes incoming emails # Returns the created object (eg. an issue, a message) or false def receive(email) @@ -78,13 +78,13 @@ User.current = @user dispatch end - + private MESSAGE_ID_RE = %r{^<redmine\.([a-z0-9_]+)\-(\d+)\.\d+@} ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]} MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]} - + def dispatch headers = [email.in_reply_to, email.references].flatten.compact if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE} @@ -100,7 +100,7 @@ elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE) receive_message_reply(m[1].to_i) else - receive_issue + dispatch_to_default end rescue ActiveRecord::RecordInvalid => e # TODO: send a email to the user @@ -113,7 +113,11 @@ logger.error "MailHandler: unauthorized attempt from #{user}" if logger false end - + + def dispatch_to_default + receive_issue + end + # Creates a new issue def receive_issue project = target_project @@ -130,7 +134,7 @@ issue.subject = '(no subject)' end issue.description = cleaned_up_text_body - + # add To and Cc as watchers before saving so the watchers can reply to Redmine add_watchers(issue) issue.save! @@ -138,7 +142,7 @@ logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info issue end - + # Adds a note to an existing issue def receive_issue_reply(issue_id) issue = Issue.find_by_id(issue_id) @@ -147,16 +151,20 @@ unless @@handler_options[:no_permission_check] raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project) end - - journal = issue.init_journal(user, cleaned_up_text_body) + + # ignore CLI-supplied defaults for new issues + @@handler_options[:issue].clear + + journal = issue.init_journal(user) issue.safe_attributes = issue_attributes_from_keywords(issue) issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} + journal.notes = cleaned_up_text_body add_attachments(issue) issue.save! logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info journal end - + # Reply will be added to the issue def receive_journal_reply(journal_id) journal = Journal.find_by_id(journal_id) @@ -164,17 +172,17 @@ receive_issue_reply(journal.journalized_id) end end - + # Receives a reply to a forum message def receive_message_reply(message_id) message = Message.find_by_id(message_id) if message message = message.root - + unless @@handler_options[:no_permission_check] raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project) end - + if !message.locked? reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip, :content => cleaned_up_text_body) @@ -188,7 +196,7 @@ end end end - + def add_attachments(obj) if email.has_attachments? email.attachments.each do |attachment| @@ -199,7 +207,7 @@ end end end - + # Adds To and Cc as watchers of the given object if the sender has the # appropriate permission def add_watchers(obj) @@ -211,7 +219,7 @@ end end end - + def get_keyword(attr, options={}) @keywords ||= {} if @keywords.has_key?(attr) @@ -226,14 +234,14 @@ end end end - + # Destructively extracts the value for +attr+ in +text+ # Returns nil if no matching keyword found def extract_keyword!(text, attr, format=nil) keys = [attr.to_s.humanize] if attr.is_a?(Symbol) - keys << l("field_#{attr}", :default => '', :locale => user.language) if user - keys << l("field_#{attr}", :default => '', :locale => Setting.default_language) + keys << l("field_#{attr}", :default => '', :locale => user.language) if user && user.language.present? + keys << l("field_#{attr}", :default => '', :locale => Setting.default_language) if Setting.default_language.present? end keys.reject! {|k| k.blank?} keys.collect! {|k| Regexp.escape(k)} @@ -250,28 +258,34 @@ 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) assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_user_from_keyword(k) assigned_to = nil if assigned_to && !issue.assignable_users.include?(assigned_to) - - { - '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), + + attrs = { + 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id), + 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id), + 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id), + 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id), 'assigned_to_id' => assigned_to.try(:id), - 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && issue.project.shared_versions.find_by_name(k).try(:id), + 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && issue.project.shared_versions.named(k).first.try(:id), 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), 'estimated_hours' => get_keyword(:estimated_hours, :override => true), 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0') }.delete_if {|k, v| v.blank? } + + if issue.new_record? && attrs['tracker_id'].nil? + attrs['tracker_id'] = issue.project.trackers.find(:first).try(:id) + end + + attrs end - + # Returns a Hash of issue custom field values extracted from keywords in the email body - def custom_field_values_from_keywords(customized) + 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 @@ -279,7 +293,7 @@ h end end - + # Returns the text/plain part of the email # If not found (eg. HTML-only email), returns the body with tags removed def plain_text_body @@ -300,7 +314,7 @@ @plain_text_body.strip! @plain_text_body end - + def cleaned_up_text_body cleanup_body(plain_text_body) end @@ -308,19 +322,19 @@ def self.full_sanitizer @full_sanitizer ||= HTML::FullSanitizer.new end - + # Creates a user account for the +email+ sender def self.create_user_from_email(email) addr = email.from_addrs.to_a.first if addr && !addr.spec.blank? user = User.new user.mail = addr.spec - + names = addr.name.blank? ? addr.spec.gsub(/@.*$/, '').split('.') : addr.name.split user.firstname = names.shift user.lastname = names.join(' ') user.lastname = '-' if user.lastname.blank? - + user.login = user.mail user.password = ActiveSupport::SecureRandom.hex(5) user.language = Setting.default_language @@ -329,7 +343,7 @@ end private - + # Removes the email body of text after the truncation configurations. def cleanup_body(body) delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}