diff app/models/mail_handler.rb @ 37:94944d00e43c

* Update to SVN trunk rev 4411
author Chris Cannam <chris.cannam@soundsoftware.ac.uk>
date Fri, 19 Nov 2010 13:24:41 +0000
parents 513646585e45
children af80e5618e9b 8661b858af72
line wrap: on
line diff
--- a/app/models/mail_handler.rb	Fri Sep 24 14:06:04 2010 +0100
+++ b/app/models/mail_handler.rb	Fri Nov 19 13:24:41 2010 +0000
@@ -17,6 +17,7 @@
 
 class MailHandler < ActionMailer::Base
   include ActionView::Helpers::SanitizeHelper
+  include Redmine::I18n
 
   class UnauthorizedAction < StandardError; end
   class MissingInformation < StandardError; end
@@ -116,36 +117,20 @@
   # Creates a new issue
   def receive_issue
     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
     unless @@handler_options[:no_permission_check]
       raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
     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)
-    # check workflow
-    if status && issue.new_statuses_allowed_to(user).include?(status)
-      issue.status = status
-    end
-    issue.subject = email.subject.chomp[0,255]
+    issue = Issue.new(:author => user, :project => project)
+    issue.safe_attributes = issue_attributes_from_keywords(issue)
+    issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
+    issue.subject = email.subject.to_s.chomp[0,255]
     if issue.subject.blank?
       issue.subject = '(no subject)'
     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
+    
     # add To and Cc as watchers before saving so the watchers can reply to Redmine
     add_watchers(issue)
     issue.save!
@@ -154,41 +139,19 @@
     issue
   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
   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)
     return unless issue
     # check permission
     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 status.nil? || user.allowed_to?(:edit_issues, issue.project)
     end
-
-    # add the note
+    
     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)
-    # 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!
     logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
     journal
@@ -255,8 +218,8 @@
       @keywords[attr]
     else
       @keywords[attr] = begin
-        if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr.to_s.humanize}[ \t]*:[ \t]*(.+)\s*$/i, '')
-          $1.strip
+        if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && (v = extract_keyword!(plain_text_body, attr, options[:format]))
+          v
         elsif !@@handler_options[:issue][attr].blank?
           @@handler_options[:issue][attr]
         end
@@ -264,6 +227,59 @@
     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)
+    end
+    keys.reject! {|k| k.blank?}
+    keys.collect! {|k| Regexp.escape(k)}
+    format ||= '.+'
+    text.gsub!(/^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i, '')
+    $2 && $2.strip
+  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)
+    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),
+      '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),
+      '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? }
+  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
   # If not found (eg. HTML-only email), returns the body with tags removed
   def plain_text_body
@@ -318,7 +334,7 @@
   def cleanup_body(body)
     delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
     unless delimiters.empty?
-      regex = Regexp.new("^(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
+      regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
       body = body.gsub(regex, '')
     end
     body.strip